Repository: softlayer/softlayer-python
Pull request #1253 (2020-04-03 22:51:50)

1239 VPN subnet access to a use


Patch:
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index f6edee47..4a326130 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -191,11 +191,9 @@
     ('loadbal:order-options', 'SoftLayer.CLI.loadbal.order:order_options'),
     ('loadbal:cancel', 'SoftLayer.CLI.loadbal.order:cancel'),
 
-
     ('loadbal:ns-detail', 'SoftLayer.CLI.loadbal.ns_detail:cli'),
     ('loadbal:ns-list', 'SoftLayer.CLI.loadbal.ns_list:cli'),
 
-
     ('metadata', 'SoftLayer.CLI.metadata:cli'),
 
     ('nas', 'SoftLayer.CLI.nas'),
@@ -304,6 +302,8 @@
     ('user:edit-details', 'SoftLayer.CLI.user.edit_details:cli'),
     ('user:create', 'SoftLayer.CLI.user.create:cli'),
     ('user:delete', 'SoftLayer.CLI.user.delete:cli'),
+    ('user:vpn-manual', 'SoftLayer.CLI.user.vpn_manual:cli'),
+    ('user:vpn-subnet', 'SoftLayer.CLI.user.vpn_subnet:cli'),
 
     ('vlan', 'SoftLayer.CLI.vlan'),
     ('vlan:detail', 'SoftLayer.CLI.vlan.detail:cli'),
diff --git a/SoftLayer/CLI/user/vpn_manual.py b/SoftLayer/CLI/user/vpn_manual.py
new file mode 100644
index 00000000..35e92f75
--- /dev/null
+++ b/SoftLayer/CLI/user/vpn_manual.py
@@ -0,0 +1,28 @@
+"""Enable or Disable vpn subnets manual config for a user."""
+# :license: MIT, see LICENSE for more details.
+
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import helpers
+
+
+@click.command()
+@click.argument('user')
+@click.option('--enable/--disable', default=True,
+              help="Enable or disable vpn subnets manual config.")
+@environment.pass_env
+def cli(env, user, enable):
+    """Enable or disable user vpn subnets manual config"""
+    mgr = SoftLayer.UserManager(env.client)
+    user_id = helpers.resolve_id(mgr.resolve_ids, user, 'username')
+
+    result = mgr.vpn_manual(user_id, enable)
+    message = "{} vpn manual config {}".format(user, 'enable' if enable else 'disable')
+
+    if result:
+        click.secho(message, fg='green')
+    else:
+        click.secho("Failed to update {}".format(user), fg='red')
diff --git a/SoftLayer/CLI/user/vpn_subnet.py b/SoftLayer/CLI/user/vpn_subnet.py
new file mode 100644
index 00000000..627d5854
--- /dev/null
+++ b/SoftLayer/CLI/user/vpn_subnet.py
@@ -0,0 +1,30 @@
+"""Add or remove specific subnets access for a user."""
+# :license: MIT, see LICENSE for more details.
+
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import helpers
+
+
+@click.command()
+@click.option('--add/--remove', default=True,
+              help="Add or remove access to subnets.")
+@click.argument('user', nargs=1, required=True)
+@click.argument('subnet', nargs=-1, required=True)
+@environment.pass_env
+def cli(env, user, add, subnet):
+    """Add or remove subnets access for a user."""
+    mgr = SoftLayer.UserManager(env.client)
+    user_id = helpers.resolve_id(mgr.resolve_ids, user, 'username')
+    if add:
+        result = mgr.vpn_subnet_add(user_id, subnet)
+    else:
+        result = mgr.vpn_subnet_remove(user_id, subnet)
+
+    if result:
+        click.secho("%s updated successfully" % (user), fg='green')
+    else:
+        click.secho("Failed to update %s" % (user), fg='red')
diff --git a/SoftLayer/fixtures/SoftLayer_Network_Service_Vpn_Overrides.py b/SoftLayer/fixtures/SoftLayer_Network_Service_Vpn_Overrides.py
new file mode 100644
index 00000000..a0c5caec
--- /dev/null
+++ b/SoftLayer/fixtures/SoftLayer_Network_Service_Vpn_Overrides.py
@@ -0,0 +1,2 @@
+createObjects = True
+deleteObjects = True
diff --git a/SoftLayer/fixtures/SoftLayer_User_Customer.py b/SoftLayer/fixtures/SoftLayer_User_Customer.py
index 4b30ba32..42c8f84c 100644
--- a/SoftLayer/fixtures/SoftLayer_User_Customer.py
+++ b/SoftLayer/fixtures/SoftLayer_User_Customer.py
@@ -63,7 +63,6 @@
      'name': 'Add/Upgrade Storage (StorageLayer)'}
 ]
 
-
 getLoginAttempts = [
     {
         "createDate": "2017-10-03T09:28:33-06:00",
@@ -74,8 +73,16 @@
     }
 ]
 
+getOverrides = [
+    {
+        'id': 3661234,
+        'subnetId': 1234
+    }
+]
+
 addBulkPortalPermission = True
 removeBulkPortalPermission = True
 createObject = getObject
 editObject = True
 addApiAuthenticationKey = True
+updateVpnUser = True
diff --git a/SoftLayer/managers/user.py b/SoftLayer/managers/user.py
index 24707138..5875d76a 100644
--- a/SoftLayer/managers/user.py
+++ b/SoftLayer/managers/user.py
@@ -34,6 +34,7 @@ class UserManager(utils.IdentifierMixin, object):
     def __init__(self, client):
         self.client = client
         self.user_service = self.client['SoftLayer_User_Customer']
+        self.override_service = self.client['Network_Service_Vpn_Overrides']
         self.account_service = self.client['SoftLayer_Account']
         self.resolvers = [self._get_id_from_username]
         self.all_permissions = None
@@ -267,6 +268,66 @@ def add_api_authentication_key(self, user_id):
         """
         return self.user_service.addApiAuthenticationKey(id=user_id)
 
+    def vpn_manual(self, user_id, value):
+        """Enable or disable the manual config of subnets.
+
+        :param int user_id: User to edit.
+        :param bool value: Value for vpnManualConfig flag.
+        """
+        user_object = {'vpnManualConfig': value}
+        return self.edit_user(user_id, user_object)
+
+    def vpn_subnet_add(self, user_id, subnet_ids):
+        """Add subnets for a user.
+
+        :param int user_id: User to edit.
+        :param list subnet_ids: list of subnet Ids.
+        """
+        overrides = [{"userId": user_id, "subnetId": subnet_id} for subnet_id in subnet_ids]
+        return_value = self.override_service.createObjects(overrides)
+        update_success = self.user_service.updateVpnUser(id=user_id)
+        if not update_success:
+            raise exceptions.SoftLayerAPIError("Overrides created, but unable to update VPN user")
+        return return_value
+
+    def vpn_subnet_remove(self, user_id, subnet_ids):
+        """Remove subnets for a user.
+
+        :param int user_id: User to edit.
+        :param list subnet_ids: list of subnet Ids.
+        """
+        overrides = self.get_overrides_list(user_id, subnet_ids)
+        return_value = self.override_service.deleteObjects(overrides)
+        update_success = self.user_service.updateVpnUser(id=user_id)
+        if not update_success:
+            raise exceptions.SoftLayerAPIError("Overrides deleted, but unable to update VPN user")
+        return return_value
+
+    def get_overrides_list(self, user_id, subnet_ids):
+        """Converts a list of subnets to a list of overrides.
+
+        :param int user_id: The ID of the user.
+        :param list subnet_ids: A list of subnets.
+        :returns: A list of overrides associated with the given subnets.
+        """
+
+        overrides_list = []
+        matching_overrides = {}
+        output_error = "Subnet {} does not exist in the subnets assigned for user {}"
+        _mask = 'mask[id,subnetId]'
+        overrides = self.user_service.getOverrides(id=user_id, mask=_mask)
+        for subnet in subnet_ids:
+            for override in overrides:
+                if int(subnet) == override.get('subnetId'):
+                    matching_overrides = override
+                    break
+            if matching_overrides.get('subnetId') is None:
+                raise exceptions.SoftLayerError(output_error.format(subnet, user_id))
+
+            overrides_list.append(matching_overrides)
+
+        return overrides_list
+
 
 def _keyname_search(haystack, needle):
     for item in haystack:
diff --git a/docs/cli/users.rst b/docs/cli/users.rst
index 5058d065..21ff1b7b 100644
--- a/docs/cli/users.rst
+++ b/docs/cli/users.rst
@@ -32,4 +32,12 @@ Version 5.6.0 introduces the ability to interact with user accounts from the cli
     :prog: user delete
     :show-nested:
 
+.. click:: SoftLayer.CLI.user.vpn-manual:cli
+    :prog: user vpn-manual
+    :show-nested:
+
+.. click:: SoftLayer.CLI.user.vpn-subnet:cli
+    :prog: user vpn-subnet
+    :show-nested:
+
 


Test patch:
diff --git a/tests/CLI/modules/user_tests.py b/tests/CLI/modules/user_tests.py
index 79554fc8..6f58c14a 100644
--- a/tests/CLI/modules/user_tests.py
+++ b/tests/CLI/modules/user_tests.py
@@ -14,7 +14,6 @@
 
 
 class UserCLITests(testing.TestCase):
-
     """User list tests"""
 
     def test_user_list(self):
@@ -153,6 +152,7 @@ def test_edit_perms_from_user(self):
         self.assert_called_with('SoftLayer_User_Customer', 'addBulkPortalPermission', identifier=11100)
 
     """User create tests"""
+
     @mock.patch('SoftLayer.CLI.formatting.confirm')
     def test_create_user(self, confirm_mock):
         confirm_mock.return_value = True
@@ -228,6 +228,7 @@ def test_create_user_from_user(self, confirm_mock):
         self.assert_called_with('SoftLayer_User_Customer', 'getObject', identifier=1234)
 
     """User edit-details tests"""
+
     @mock.patch('SoftLayer.CLI.user.edit_details.click')
     def test_edit_details(self, click):
         result = self.run_command(['user', 'edit-details', '1234', '-t', '{"firstName":"Supermand"}'])
@@ -252,6 +253,7 @@ def test_edit_details_bad_json(self):
         self.assertEqual(result.exit_code, 2)
 
     """User delete tests"""
+
     @mock.patch('SoftLayer.CLI.user.delete.click')
     def test_delete(self, click):
         result = self.run_command(['user', 'delete', '12345'])
@@ -269,3 +271,37 @@ def test_delete_failure(self, click):
         self.assert_no_fail(result)
         self.assert_called_with('SoftLayer_User_Customer', 'editObject',
                                 args=({'userStatusId': 1021},), identifier=12345)
+
+    """User vpn manual config tests"""
+
+    @mock.patch('SoftLayer.CLI.user.vpn_manual.click')
+    def test_vpn_manual(self, click):
+        result = self.run_command(['user', 'vpn-manual', '12345', '--enable'])
+        click.secho.assert_called_with('12345 vpn manual config enable', fg='green')
+        self.assert_no_fail(result)
+
+    def test_vpn_manual_fail(self):
+        mock = self.set_mock('SoftLayer_User_Customer', 'editObject')
+        mock.return_value = False
+        result = self.run_command(['user', 'vpn-manual', '12345', '--enable'])
+        self.assert_no_fail(result)
+
+    """User vpn subnet tests"""
+
+    @mock.patch('SoftLayer.CLI.user.vpn_subnet.click')
+    def test_vpn_subnet_add(self, click):
+        result = self.run_command(['user', 'vpn-subnet', '12345', '--add', '1234'])
+        click.secho.assert_called_with('12345 updated successfully', fg='green')
+        self.assert_no_fail(result)
+
+    def test_vpn_subnet_add_fail(self):
+        mock = self.set_mock('SoftLayer_Network_Service_Vpn_Overrides', 'createObjects')
+        mock.return_value = False
+        result = self.run_command(['user', 'vpn-subnet', '12345', '--add', '1234'])
+        self.assert_no_fail(result)
+
+    @mock.patch('SoftLayer.CLI.user.vpn_subnet.click')
+    def test_vpn_subnet_remove(self, click):
+        result = self.run_command(['user', 'vpn-subnet', '12345', '--remove', '1234'])
+        click.secho.assert_called_with('12345 updated successfully', fg='green')
+        self.assert_no_fail(result)
diff --git a/tests/managers/user_tests.py b/tests/managers/user_tests.py
index 66443de0..b75a5a77 100644
--- a/tests/managers/user_tests.py
+++ b/tests/managers/user_tests.py
@@ -9,7 +9,6 @@
 from SoftLayer import exceptions
 from SoftLayer import testing
 
-
 real_datetime_class = datetime.datetime
 
 
@@ -18,6 +17,7 @@ def mock_datetime(target, datetime_module):
 
     https://solidgeargroup.com/mocking-the-time
     """
+
     class DatetimeSubclassMeta(type):
         @classmethod
         def __instancecheck__(mcs, obj):
@@ -106,7 +106,6 @@ def test_get_logins_default(self):
     def test_get_events_default(self):
         target = datetime.datetime(2018, 5, 15)
         with mock_datetime(target, datetime):
-
             self.manager.get_events(1234)
             expected_filter = {
                 'userId': {
@@ -221,3 +220,29 @@ def test_create_user_handle_paas_exception(self):
             self.assertEqual(ex.args[0], "Your request for a new user was received, but it needs to be processed by "
                                          "the Platform Services API first. Barring any errors on the Platform Services "
                                          "side, your new user should be created shortly.")
+
+    def test_vpn_manual(self):
+        user_id = 1234
+        self.manager.vpn_manual(user_id, True)
+        self.assert_called_with('SoftLayer_User_Customer', 'editObject', identifier=user_id)
+
+    def test_vpn_subnet_add(self):
+        user_id = 1234
+        subnet_id = 1234
+        expected_args = (
+            [{"userId": user_id, "subnetId": subnet_id}],
+            )
+        self.manager.vpn_subnet_add(user_id, [subnet_id])
+        self.assert_called_with('SoftLayer_Network_Service_Vpn_Overrides', 'createObjects', args=expected_args)
+        self.assert_called_with('SoftLayer_User_Customer', 'updateVpnUser', identifier=user_id)
+
+    def test_vpn_subnet_remove(self):
+        user_id = 1234
+        subnet_id = 1234
+        overrides = [{'id': 3661234, 'subnetId': subnet_id}]
+        expected_args = (
+            overrides,
+            )
+        self.manager.vpn_subnet_remove(user_id, [subnet_id])
+        self.assert_called_with('SoftLayer_Network_Service_Vpn_Overrides', 'deleteObjects', args=expected_args)
+        self.assert_called_with('SoftLayer_User_Customer', 'updateVpnUser', identifier=user_id)