From d0d160b9ba32489a0e63fd611a7da2cfc500dd96 Mon Sep 17 00:00:00 2001
From: Cyril Jouve <jv.cyril@gmail.com>
Date: Mon, 10 Aug 2020 00:27:11 +0200
Subject: [PATCH] use python3 ipaddress module

---
 .../browser/server_groups/servers/__init__.py | 13 ++--
 .../servers/tests/test_is_valid_address.py    | 39 ++++++++++++
 .../browser/server_groups/servers/utils.py    |  9 +++
 web/pgadmin/utils/ip.py                       | 61 -------------------
 4 files changed, 54 insertions(+), 68 deletions(-)
 create mode 100644 web/pgadmin/browser/server_groups/servers/tests/test_is_valid_address.py
 delete mode 100644 web/pgadmin/utils/ip.py

diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index 78200b69e..567c5afae 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -19,7 +19,6 @@ from pgadmin.utils.ajax import make_json_response, bad_request, forbidden, \
     make_response as ajax_response, internal_server_error, unauthorized, gone
 from pgadmin.utils.crypto import encrypt, decrypt, pqencryptpassword
 from pgadmin.utils.menu import MenuItem
-from pgadmin.utils.ip import is_valid_ipaddress
 from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
 
 import config
@@ -30,7 +29,7 @@ from pgadmin.utils.master_password import get_crypt_key
 from pgadmin.utils.exception import CryptKeyMissing
 from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
 from psycopg2 import Error as psycopg2_Error, OperationalError
-
+from pgadmin.browser.server_groups.servers.utils import is_valid_ipaddress
 
 def has_any(data, keys):
     """
@@ -523,8 +522,8 @@ class ServerNode(PGChildNodeView):
         if 'db_res' in data:
             data['db_res'] = ','.join(data['db_res'])
 
-        if 'hostaddr' in data and data['hostaddr'] and data['hostaddr'] != '' \
-                and not is_valid_ipaddress(data['hostaddr']):
+        hostaddr = data.get('hostaddr')
+        if hostaddr and not is_valid_ipaddress(hostaddr):
             return make_json_response(
                 success=0,
                 status=400,
@@ -755,8 +754,8 @@ class ServerNode(PGChildNodeView):
                     ).format(arg)
                 )
 
-        if 'hostaddr' in data and data['hostaddr'] and data['hostaddr'] != '' \
-                and not is_valid_ipaddress(data['hostaddr']):
+        hostaddr = data.get('hostaddr')
+        if hostaddr and not is_valid_ipaddress(data['hostaddr']):
             return make_json_response(
                 success=0,
                 status=400,
@@ -774,7 +773,7 @@ class ServerNode(PGChildNodeView):
                 servergroup_id=data.get('gid', gid),
                 name=data.get('name'),
                 host=data.get('host', None),
-                hostaddr=data.get('hostaddr', None),
+                hostaddr=hostaddr,
                 port=data.get('port'),
                 maintenance_db=data.get('db', None),
                 username=data.get('username'),
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_is_valid_address.py b/web/pgadmin/browser/server_groups/servers/tests/test_is_valid_address.py
new file mode 100644
index 000000000..7a5b48a69
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_is_valid_address.py
@@ -0,0 +1,39 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+from pgadmin.utils.route import BaseTestGenerator
+from .. import utils
+
+
+class IsValidAddressTestCase(BaseTestGenerator):
+    scenarios = [
+        ('TestCase for Valid Ipv4 Address', {
+            'address': '192.168.0.1',
+            'respdata': True
+        }),
+        ('TestCase for Valid Ipv6 Address', {
+            'address': '2001:db8::',
+            'respdata': True
+        }),
+        ('TestCase for Invalid Ip Address', {
+            'address': 'toto',
+            'respdata': False
+        }),
+    ]
+
+    @classmethod
+    def setUpClass(cls):
+        pass
+
+    def runTest(self):
+        self.assertEquals(utils.is_valid_ipaddress(self.address), self.respdata)
+
+    @classmethod
+    def tearDownClass(cls):
+        pass
diff --git a/web/pgadmin/browser/server_groups/servers/utils.py b/web/pgadmin/browser/server_groups/servers/utils.py
index a76c33788..59fa30ce7 100644
--- a/web/pgadmin/browser/server_groups/servers/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/utils.py
@@ -8,11 +8,20 @@
 ##########################################################################
 
 """Server helper utilities"""
+from ipaddress import ip_address
+
 from pgadmin.utils.crypto import encrypt, decrypt
 import config
 from pgadmin.model import db, Server
 
 
+def is_valid_ipaddress(address):
+    try:
+        return bool(ip_address(address))
+    except ValueError:
+        return False
+
+
 def parse_priv_from_db(db_privileges):
     """
     Common utility function to parse privileges retrieved from database.
diff --git a/web/pgadmin/utils/ip.py b/web/pgadmin/utils/ip.py
deleted file mode 100644
index 7d969f360..000000000
--- a/web/pgadmin/utils/ip.py
+++ /dev/null
@@ -1,61 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2020, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-#########################################################################
-
-"""This File Provides ipv4 and ipv6 address check."""
-
-import re
-
-IPV4SEG = r'(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])'
-IPV4ADDR = r'(?:(?:' + IPV4SEG + r'\.){3,3}' + IPV4SEG + r')'
-IPV6SEG = r'(?:(?:[0-9a-fA-F]){1,4})'
-IPV6GROUPS = (
-    # 1:2:3:4:5:6:7:8
-    r'(?:' + IPV6SEG + r':){7,7}' + IPV6SEG,
-    # 1::                                 1:2:3:4:5:6:7::
-    r'(?:' + IPV6SEG + r':){1,7}:',
-    # 1::8               1:2:3:4:5:6::8   1:2:3:4:5:6::8
-    r'(?:' + IPV6SEG + r':){1,6}:' + IPV6SEG,
-    # 1::7:8             1:2:3:4:5::7:8   1:2:3:4:5::8
-    r'(?:' + IPV6SEG + r':){1,5}(?::' + IPV6SEG + r'){1,2}',
-    # 1::6:7:8           1:2:3:4::6:7:8   1:2:3:4::8
-    r'(?:' + IPV6SEG + r':){1,4}(?::' + IPV6SEG + r'){1,3}',
-    # 1::5:6:7:8         1:2:3::5:6:7:8   1:2:3::8
-    r'(?:' + IPV6SEG + r':){1,3}(?::' + IPV6SEG + r'){1,4}',
-    # 1::4:5:6:7:8       1:2::4:5:6:7:8   1:2::8
-    r'(?:' + IPV6SEG + r':){1,2}(?::' + IPV6SEG + r'){1,5}',
-    # 1::3:4:5:6:7:8     1::3:4:5:6:7:8   1::8
-    IPV6SEG + r':(?:(?::' + IPV6SEG + r'){1,6})',
-    # ::2:3:4:5:6:7:8    ::2:3:4:5:6:7:8  ::8       ::
-    r':(?:(?::' + IPV6SEG + r'){1,7}|:)',
-    # fe80::7:8%eth0   fe80::7:8%1  (link-local IPv6 addresses with zone index)
-    r'fe80:(?::' + IPV6SEG + r'){0,4}%[0-9a-zA-Z]{1,}',
-    # ::255.255.255.255  ::ffff:255.255.255.255  ::ffff:0:255.255.255.255
-    # (IPv4-mapped IPv6 addresses and IPv4-translated addresses)
-    r'::(?:ffff(?::0{1,4}){0,1}:){0,1}[^\s:]' + IPV4ADDR,
-    # 2001:db8:3:4::192.0.2.33  64:ff9b::192.0.2.33
-    # (IPv4-Embedded IPv6 Address)
-    r'(?:' + IPV6SEG + r':){1,4}:[^\s:]' + IPV4ADDR,
-)
-# Reverse rows for greedy match
-IPV6ADDR = '|'.join(['(?:{})'.format(g) for g in IPV6GROUPS[::-1]])
-
-ip6re = re.compile(IPV6ADDR)
-ip4re = re.compile(IPV4ADDR)
-
-
-def is_valid_ip4address(addr):
-    return ip4re.match(addr)
-
-
-def is_valid_ip6address(addr):
-    return ip6re.match(addr)
-
-
-def is_valid_ipaddress(addr):
-    return ip4re.match(addr) or is_valid_ip6address(addr)
-- 
2.27.0

