public inbox for [email protected]  
help / color / mirror / Atom feed
From: Pradip Parkale <[email protected]>
To: Akshay Joshi <[email protected]>
Cc: pgadmin-hackers <[email protected]>
Subject: Re: [pgAdmin][RM4979]: Configuration files for data sources or similar.
Date: Thu, 3 Sep 2020 12:25:14 +0530
Message-ID: <CAJ9T6StLHKviij7ZPXC36amgYy=tsMvKqW48oTxzUbkUaFx3gQ@mail.gmail.com> (raw)
In-Reply-To: <CANxoLDdQP=kHtxrj-TymBd98kemxQvOkthvUoaitbMFRneRHXg@mail.gmail.com>
References: <CAJ9T6SvOzSPGyRgVg+8XksV_4=mOSSMoy8RaGKm0O9qFTT_ZQQ@mail.gmail.com>
	<CANxoLDcmxQsdmO-8Z9O6=zmuUs5KGkXzCmjqyoatCRCd=xBkdA@mail.gmail.com>
	<CAJ9T6SsV50gwjNP3D4YhONhtV6=RfgqE4hUGoDf91aBZMcahsA@mail.gmail.com>
	<CANxoLDdVUbL7Eeen5jNX9ZrpvOe1zGqi_XCw8ECFzeJv8oJLCg@mail.gmail.com>
	<CAJ9T6Su5LAOO=JW-ZpLs6wGUta7PMZ52wzcQWmSCPbSSR+U8Gw@mail.gmail.com>
	<CANxoLDdQP=kHtxrj-TymBd98kemxQvOkthvUoaitbMFRneRHXg@mail.gmail.com>

Hi Akshay,
Please find the updated patch.

On Wed, Sep 2, 2020 at 1:48 PM Akshay Joshi <[email protected]>
wrote:

> Hi Pradip
>
> Please fix SonarQube issues that introduce with this patch.
>
> On Tue, Sep 1, 2020 at 4:47 PM Pradip Parkale <
> [email protected]> wrote:
>
>> Hi Akshay,
>>
>> Please find the updated patch.
>>
>> On Tue, Aug 25, 2020 at 5:53 PM Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Hi Pradip
>>>
>>> Following are the GUI review comments:
>>> *Testing Scenario*: I have three users U1 and U2 are Admin and U3 is a
>>> normal user. Login from U1 first time and pgAdmin4 discover all the local
>>> servers. Steps that I perform:
>>>
>>>    - Shared one server.
>>>    - Create a group 'Test' and move one server to that group.
>>>    - Share the server of the 'Test' group as well.
>>>
>>>              [image: Screenshot 2020-08-25 at 4.38.32 PM.png]
>>> *Issues:*
>>>
>>>    - When login using U2 or U3 PostgreSQL 9.2 and 10 shown twice which
>>>    should not. Refer below screenshot
>>>     [image: Screenshot 2020-08-25 at 4.30.22 PM.png]
>>>
>>> Fixed. If the server is auto-discovered and U1 shared the
>> auto-discovered server(PostgreSQL 9.2 in the above example) and then log in
>> using U2, in this case, shared server(PostgreSQL 9.2) will not visible to
>> U2 as the same server will be auto-discovered for U2.
>>
>>>
>>>    -
>>>    - When expanding the server or clicking on the 'Connect Server' menu
>>>    of the shared server, it opens the properties dialog.
>>>
>>> This is expected. Properties dialogue will open the first time for a
>> shared server where the user has to enter server details. This will happen
>> for the user who is not the owner of the server.
>>
>>>
>>>    - Try to update the IPAddress, Port it is not updating for the
>>>    shared server. Check other properties too.
>>>
>>> Fixed.
>>
>>>
>>>    -
>>>    - When we set "Hide shared server?" setting to true/false. A
>>>    complete browser tree should be refreshed. With the current implementation,
>>>    each group needs to be refreshed individually and the server group still
>>>    visible while refreshing the complete page Browser Tree looks perfect.
>>>
>>> Fixed.
>>
>>>
>>>    - When there is only one server in a group and we expanded the group
>>>    'Connect to server' dialog appears which should not. (*This may be an old
>>>    issue)
>>>
>>> This is expected and its an old behavior.
>>
>>>
>>>    -
>>>    - Schema Diff shared servers are not visible inside the appropriate
>>>    group in the source and target server list.
>>>
>>> Fixed
>>
>>>
>>>    - Documentation changes required (Create a new Redmine).
>>>
>>> Done.
>>
>>>
>>>    -
>>>    - SonarQube shows 1 new Bug and 4 new code smell. (Fixed the Bug,
>>>    code smell can be fixed later)
>>>
>>> Fixed.
>>
>>>
>>>    - 'pgAdmin4_test_non_admin_credentials' should be added in '
>>>    test_config.json.in' file.
>>>
>>> Fixed.
>>
>>>
>>>    - For Desktop mode shared server test cases should be skipped. It is
>>>    failing at the moment.
>>>
>>> Fixed.
>>
>>> Code review still remains :)
>>>
>>> On Fri, Aug 21, 2020 at 6:00 PM Pradip Parkale <
>>> [email protected]> wrote:
>>>
>>>> Hi Akshay,
>>>> Please find the updated patch.
>>>>
>>>> On Fri, Aug 21, 2020 at 1:55 PM Akshay Joshi <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Pradip
>>>>>
>>>>> The patch is not applied, can you please rebase and send it again.
>>>>>
>>>>> On Thu, Aug 20, 2020 at 3:58 PM Pradip Parkale <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Hackers,
>>>>>>
>>>>>> Please find the attached patch for the shared server implementation.
>>>>>>
>>>>>> Few key points:
>>>>>>
>>>>>>    1. The admin who is the owner of the server user can share the
>>>>>>    server with other users.
>>>>>>    2. This option will be available only for admin users.
>>>>>>    3. If the user doesn't want to see the shared server then the
>>>>>>    option to hide the shared server is available in preferences.
>>>>>>    4. The user who is not the owner of the server, can't delete the
>>>>>>    shared server and server group.
>>>>>>    5. This option is only available in server mode.
>>>>>>
>>>>>>
>>>>>> --
>>>>>> Thanks & Regards,
>>>>>> Pradip Parkale
>>>>>> Software Engineer | EnterpriseDB Corporation
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> *Thanks & Regards*
>>>>> *Akshay Joshi*
>>>>> *pgAdmin Hacker | Sr. Software Architect*
>>>>> *EDB Postgres <http://edbpostgres.com>*
>>>>>
>>>>> *Mobile: +91 976-788-8246*
>>>>>
>>>>
>>>>
>>>> --
>>>> Thanks & Regards,
>>>> Pradip Parkale
>>>> Software Engineer | EnterpriseDB Corporation
>>>>
>>>
>>>
>>> --
>>> *Thanks & Regards*
>>> *Akshay Joshi*
>>> *pgAdmin Hacker | Sr. Software Architect*
>>> *EDB Postgres <http://edbpostgres.com>*
>>>
>>> *Mobile: +91 976-788-8246*
>>>
>>
>>
>> --
>> Thanks & Regards,
>> Pradip Parkale
>> Software Engineer | EnterpriseDB Corporation
>>
>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Sr. Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>


-- 
Thanks & Regards,
Pradip Parkale
Software Engineer | EnterpriseDB Corporation


Attachments:

  [image/png] Screenshot 2020-08-25 at 4.30.22 PM.png (70.2K, 3-Screenshot%202020-08-25%20at%204.30.22%20PM.png)
  download | view image

  [image/png] Screenshot 2020-08-25 at 4.38.32 PM.png (52.9K, 4-Screenshot%202020-08-25%20at%204.38.32%20PM.png)
  download | view image

  [application/octet-stream] RM4979_v4.patch (142.1K, 5-RM4979_v4.patch)
  download | inline diff:
diff --git a/web/migrations/versions/a091c9611d20_.py b/web/migrations/versions/a091c9611d20_.py
new file mode 100644
index 000000000..d9da2b10f
--- /dev/null
+++ b/web/migrations/versions/a091c9611d20_.py
@@ -0,0 +1,72 @@
+
+"""empty message
+
+Revision ID: a091c9611d20
+Revises: 84700139beb0
+Create Date: 2020-07-14 17:20:22.705737
+
+"""
+from pgadmin.model import db
+
+
+# revision identifiers, used by Alembic.
+revision = 'a091c9611d20'
+down_revision = '84700139beb0'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    db.engine.execute(
+        'ALTER TABLE server ADD COLUMN shared BOOLEAN'
+    )
+
+    db.engine.execute("""
+            CREATE TABLE sharedserver (
+            id	INTEGER NOT NULL,
+            user_id	INTEGER NOT NULL,
+            server_owner VARCHAR(64),
+            servergroup_id	INTEGER NOT NULL,
+            name	VARCHAR(128) NOT NULL,
+            host	VARCHAR(128),
+            port	INTEGER NOT NULL CHECK(port >= 1 AND port <= 65534),
+            maintenance_db	VARCHAR(64),
+            username	VARCHAR(64),
+            password	VARCHAR(64),
+            role	VARCHAR(64),
+            ssl_mode	VARCHAR(16) NOT NULL CHECK(ssl_mode IN
+                ( 'allow' , 'prefer' , 'require' , 'disable' ,
+                  'verify-ca' , 'verify-full' )
+            ),
+            comment	VARCHAR(1024),
+            discovery_id	VARCHAR(128),
+            hostaddr	TEXT(1024),
+            db_res	TEXT,
+            passfile	TEXT,
+            sslcert	TEXT,
+            sslkey	TEXT,
+            sslrootcert	TEXT,
+            sslcrl	TEXT,
+            sslcompression	INTEGER DEFAULT 0,
+            bgcolor TEXT(10),
+            fgcolor TEXT(10),
+            service TEXT,
+            use_ssh_tunnel INTEGER DEFAULT 0,
+            tunnel_host TEXT,
+            tunnel_port TEXT,
+            tunnel_username TEXT,
+            tunnel_authentication INTEGER DEFAULT 0,
+            tunnel_identity_file TEXT,
+            shared BOOLEAN NOT NULL,
+            save_password BOOLEAN NOT NULL,
+            tunnel_password VARCHAR(64),
+            connect_timeout INTEGER ,
+            PRIMARY KEY(id),
+            FOREIGN KEY(user_id) REFERENCES user(id),
+            FOREIGN KEY(servergroup_id) REFERENCES servergroup(id)
+        );
+    """)
+
+
+def downgrade():
+    pass
diff --git a/web/pgadmin/__init__.py b/web/pgadmin/__init__.py
index 516ba2c4d..1a712151a 100644
--- a/web/pgadmin/__init__.py
+++ b/web/pgadmin/__init__.py
@@ -28,7 +28,7 @@ from werkzeug.datastructures import ImmutableDict
 from werkzeug.local import LocalProxy
 from werkzeug.utils import find_modules
 
-from pgadmin.model import db, Role, Server, ServerGroup, \
+from pgadmin.model import db, Role, Server, SharedServer, ServerGroup, \
     User, Keys, Version, SCHEMA_VERSION as CURRENT_SCHEMA_VERSION
 from pgadmin.utils import PgAdminModule, driver, KeyManager
 from pgadmin.utils.preferences import Preferences
diff --git a/web/pgadmin/browser/register_browser_preferences.py b/web/pgadmin/browser/register_browser_preferences.py
index 948be41b8..215421cbf 100644
--- a/web/pgadmin/browser/register_browser_preferences.py
+++ b/web/pgadmin/browser/register_browser_preferences.py
@@ -9,6 +9,7 @@
 from flask_babelex import gettext
 from pgadmin.utils.constants import PREF_LABEL_DISPLAY,\
     PREF_LABEL_KEYBOARD_SHORTCUTS
+import config
 
 LOCK_LAYOUT_LEVEL = {
     'PREVENT_DOCKING': 'docking',
@@ -23,6 +24,15 @@ def register_browser_preferences(self):
         gettext("Show system objects?"), 'boolean', False,
         category_label=PREF_LABEL_DISPLAY
     )
+    if config.SERVER_MODE:
+        self.hide_shared_server = self.preference.register(
+            'display', 'hide_shared_server',
+            gettext("Hide shared server?"), 'boolean', False,
+            category_label=gettext('Display'),
+            help_str=gettext(
+                'If set to true, then all shared server will be hidden'
+            )
+        )
 
     self.preference.register(
         'display', 'enable_acitree_animation',
diff --git a/web/pgadmin/browser/server_groups/__init__.py b/web/pgadmin/browser/server_groups/__init__.py
index 717795bde..cbb3fde6a 100644
--- a/web/pgadmin/browser/server_groups/__init__.py
+++ b/web/pgadmin/browser/server_groups/__init__.py
@@ -13,7 +13,7 @@ import simplejson as json
 from abc import ABCMeta, abstractmethod
 
 import six
-from flask import request, jsonify
+from flask import request, jsonify, render_template
 from flask_babelex import gettext
 from flask_security import current_user, login_required
 from pgadmin.browser import BrowserPluginModule
@@ -22,7 +22,27 @@ from pgadmin.utils.ajax import make_json_response, gone, \
     make_response as ajax_response, bad_request
 from pgadmin.utils.menu import MenuItem
 from sqlalchemy import exc
-from pgadmin.model import db, ServerGroup
+from pgadmin.model import db, ServerGroup, Server
+import config
+from pgadmin.utils.preferences import Preferences
+
+
+def get_icon_css_class(group_id, group_user_id,
+                       default_val='icon-server_group'):
+    """
+    Returns css value
+    :param group_id:
+    :param group_user_id:
+    :param default_val:
+    :return: default_val
+    """
+    if (config.SERVER_MODE and
+        group_user_id != current_user.id and
+            ServerGroupModule.has_shared_server(group_id)):
+        default_val = 'icon-server_group_shared'
+
+    return default_val
+
 
 SG_NOT_FOUND_ERROR = 'The specified server group could not be found.'
 
@@ -31,19 +51,50 @@ class ServerGroupModule(BrowserPluginModule):
     _NODE_TYPE = "server_group"
     node_icon = "icon-%s" % _NODE_TYPE
 
+    @property
+    def csssnippets(self):
+        """
+        Returns a snippet of css to include in the page
+        """
+        snippets = [render_template("css/server_group.css")]
+
+        for submodule in self.submodules:
+            snippets.extend(submodule.csssnippets)
+
+        return snippets
+
+    @staticmethod
+    def has_shared_server(gid):
+        """
+        To check whether given server group contains shared server or not
+        :param gid:
+        :return: True if servergroup contains shared server else false
+        """
+        servers = Server.query.filter_by(servergroup_id=gid)
+        for s in servers:
+            if s.shared:
+                return True
+        return False
+
     def get_nodes(self, *arg, **kwargs):
         """Return a JSON document listing the server groups for the user"""
-        groups = ServerGroup.query.filter_by(
-            user_id=current_user.id
-        ).order_by("id")
+
+        if config.SERVER_MODE:
+            groups = ServerGroupView.get_all_server_groups()
+        else:
+            groups = ServerGroup.query.filter_by(
+                user_id=current_user.id
+            ).order_by("id")
+
         for idx, group in enumerate(groups):
             yield self.generate_browser_node(
                 "%d" % (group.id), None,
                 group.name,
-                self.node_icon,
+                get_icon_css_class(group.id, group.user_id),
                 True,
                 self.node_type,
-                can_delete=True if idx > 0 else False
+                can_delete=True if idx > 0 else False,
+                user_id=group.user_id
             )
 
     @property
@@ -196,7 +247,7 @@ class ServerGroupView(NodeView):
                 gid,
                 None,
                 servergroup.name,
-                self.node_icon,
+                get_icon_css_class(gid, servergroup.user_id),
                 True,
                 self.node_type,
                 can_delete=True  # This is user created hence can deleted
@@ -207,10 +258,7 @@ class ServerGroupView(NodeView):
     def properties(self, gid):
         """Update the server-group properties"""
 
-        # There can be only one record at most
-        sg = ServerGroup.query.filter_by(
-            user_id=current_user.id,
-            id=gid).first()
+        sg = ServerGroup.query.filter(ServerGroup.id == gid).first()
 
         if sg is None:
             return make_json_response(
@@ -220,7 +268,7 @@ class ServerGroupView(NodeView):
             )
         else:
             return ajax_response(
-                response={'id': sg.id, 'name': sg.name},
+                response={'id': sg.id, 'name': sg.name, 'user_id': sg.user_id},
                 status=200
             )
 
@@ -246,7 +294,7 @@ class ServerGroupView(NodeView):
                         "%d" % sg.id,
                         None,
                         sg.name,
-                        self.node_icon,
+                        get_icon_css_class(sg.id, sg.user_id),
                         True,
                         self.node_type,
                         # This is user created hence can deleted
@@ -292,13 +340,41 @@ class ServerGroupView(NodeView):
     def dependents(self, gid):
         return make_json_response(status=422)
 
+    @staticmethod
+    def get_all_server_groups():
+        """
+        Returns the list of server groups to show in server mode and
+        if there is any shared server in the group.
+        :return: server groups
+        """
+
+        # Don't display shared server if user has
+        # selected 'Hide shared server'
+        pref = Preferences.module('browser')
+        hide_shared_server = pref.preference('hide_shared_server').get()
+
+        server_groups = ServerGroup.query.all()
+        groups = []
+        for group in server_groups:
+            if hide_shared_server and \
+                ServerGroupModule.has_shared_server(group.id) and \
+                    group.user_id != current_user.id:
+                continue
+            if group.user_id == current_user.id or \
+                    ServerGroupModule.has_shared_server(group.id):
+                groups.append(group)
+        return groups
+
     @login_required
     def nodes(self, gid=None):
         """Return a JSON document listing the server groups for the user"""
         nodes = []
-
         if gid is None:
-            groups = ServerGroup.query.filter_by(user_id=current_user.id)
+            if config.SERVER_MODE:
+
+                groups = self.get_all_server_groups()
+            else:
+                groups = ServerGroup.query.filter_by(user_id=current_user.id)
 
             for group in groups:
                 nodes.append(
@@ -306,14 +382,14 @@ class ServerGroupView(NodeView):
                         "%d" % group.id,
                         None,
                         group.name,
-                        self.node_icon,
+                        get_icon_css_class(group.id, group.user_id),
                         True,
                         self.node_type
                     )
                 )
         else:
-            group = ServerGroup.query.filter_by(user_id=current_user.id,
-                                                id=gid).first()
+            group = ServerGroup.query.filter(ServerGroup.id == gid).first()
+
             if not group:
                 return gone(
                     errormsg=gettext("Could not find the server group.")
@@ -322,7 +398,7 @@ class ServerGroupView(NodeView):
             nodes = self.blueprint.generate_browser_node(
                 "%d" % (group.id), None,
                 group.name,
-                self.node_icon,
+                get_icon_css_class(group.id, group.user_id),
                 True,
                 self.node_type
             )
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index 2eb058a40..ed8fae0f0 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -23,7 +23,7 @@ from pgadmin.tools.sqleditor.utils.query_history import QueryHistory
 
 import config
 from config import PG_DEFAULT_DRIVER
-from pgadmin.model import db, Server, ServerGroup, User
+from pgadmin.model import db, Server, ServerGroup, User, SharedServer
 from pgadmin.utils.driver import get_driver
 from pgadmin.utils.master_password import get_crypt_key
 from pgadmin.utils.exception import CryptKeyMissing
@@ -32,6 +32,8 @@ from psycopg2 import Error as psycopg2_Error, OperationalError
 from pgadmin.browser.server_groups.servers.utils import is_valid_ipaddress
 from pgadmin.utils.constants import UNAUTH_REQ, MIMETYPE_APP_JS, \
     SERVER_CONNECTION_CLOSED
+from sqlalchemy import or_
+from pgadmin.utils.preferences import Preferences
 
 
 def has_any(data, keys):
@@ -65,6 +67,19 @@ def recovery_state(connection, postgres_version):
     return status, result, in_recovery, wal_paused
 
 
+def get_preferences():
+    """
+    Get preferences setting
+    :return: whether to hide shared server or not.
+    """
+    hide_shared_server = None
+    if config.SERVER_MODE:
+        pref = Preferences.module('browser')
+        hide_shared_server = pref.preference('hide_shared_server').get()
+
+    return hide_shared_server
+
+
 def server_icon_and_background(is_connected, manager, server):
     """
 
@@ -92,6 +107,10 @@ def server_icon_and_background(is_connected, manager, server):
         return 'icon-{0}{1}'.format(
             manager.server_type, server_background_color
         )
+    elif server.shared and config.SERVER_MODE:
+        return 'icon-shared-server-not-connected{0}'.format(
+            server_background_color
+        )
     else:
         return 'icon-server-not-connected{0}'.format(
             server_background_color
@@ -114,25 +133,93 @@ class ServerModule(sg.ServerGroupPluginModule):
         """
         return sg.ServerGroupModule.node_type
 
+    @staticmethod
+    def get_shared_server_properties(server, sharedserver):
+        """
+        Return shared server properties
+        :param server:
+        :param sharedserver:
+        :return:
+        """
+
+        server.bgcolor = sharedserver.bgcolor
+        server.fgcolor = sharedserver.fgcolor
+        server.name = sharedserver.name
+        server.role = sharedserver.role
+        server.tunnel_username = sharedserver.tunnel_username
+        server.tunnel_password = sharedserver.tunnel_password
+        server.save_password = sharedserver.save_password
+        server.passfile = sharedserver.passfile
+        server.servergroup_id = sharedserver.servergroup_id
+        server.sslcert = sharedserver.sslcert
+        server.username = sharedserver.username
+        server.server_owner = sharedserver.server_owner
+
+        return server
+
+    @staticmethod
+    def check_to_hide_shared_server(hide_shared_server, shared_server,
+                                    auto_detected_server):
+
+        hide_server = False
+        if hide_shared_server or \
+                shared_server.name == auto_detected_server:
+            hide_server = True
+
+        return hide_server
+
     @login_required
     def get_nodes(self, gid):
         """Return a JSON document listing the server groups for the user"""
-        servers = Server.query.filter_by(user_id=current_user.id,
-                                         servergroup_id=gid)
+
+        hide_shared_server = get_preferences()
+
+        servers = Server.query.filter(
+            or_(Server.user_id == current_user.id, Server.shared),
+            Server.servergroup_id == gid)
 
         driver = get_driver(PG_DEFAULT_DRIVER)
 
         for server in servers:
+
+            if server.shared and server.user_id != current_user.id:
+
+                shared_server, auto_detected_server = \
+                    self.get_shared_server(server, gid)
+
+                if self.check_to_hide_shared_server(hide_shared_server,
+                                                    shared_server,
+                                                    auto_detected_server):
+                    # Don't include shared server if hide shared server is
+                    # set to true
+                    continue
+
+                # if hide_shared_server or \
+                #         shared_server.name == auto_detected_server:
+                #     # Don't include shared server if hide shared server is
+                #     # set to true
+                #     continue
+
+                # if shared_server.name == auto_detected_server:
+                #     continue
+                server = self.get_shared_server_properties(server,
+                                                           shared_server)
             connected = False
             manager = None
             errmsg = None
             was_connected = False
             in_recovery = None
             wal_paused = None
+            server_type = 'pg'
+            user_info = None
             try:
                 manager = driver.connection_manager(server.id)
                 conn = manager.connection()
                 was_connected = conn.wasConnected
+                connected = conn.connected()
+                if connected:
+                    server_type = manager.server_type
+                    user_info = manager.user_info
             except CryptKeyMissing:
                 # show the nodes at least even if not able to connect.
                 pass
@@ -148,17 +235,20 @@ class ServerModule(sg.ServerGroupPluginModule):
                 True,
                 self.node_type,
                 connected=connected,
-                server_type=manager.server_type if connected else "pg",
+                server_type=server_type,
                 version=manager.version,
                 db=manager.db,
-                user=manager.user_info if connected else None,
+                user=user_info,
                 in_recovery=in_recovery,
                 wal_pause=wal_paused,
                 is_password_saved=bool(server.save_password),
                 is_tunnel_password_saved=True
                 if server.tunnel_password is not None else False,
                 was_connected=was_connected,
-                errmsg=errmsg
+                errmsg=errmsg,
+                user_id=server.user_id,
+                user_name=server.username,
+                shared=server.shared
             )
 
     @property
@@ -230,6 +320,82 @@ class ServerModule(sg.ServerGroupPluginModule):
     def get_exposed_url_endpoints(self):
         return ['NODE-server.connect_id']
 
+    @staticmethod
+    def create_shared_server(data, gid):
+        """
+        Create shared server
+        :param data:
+        :param gid:
+        :return: None
+        """
+
+        shared_server = None
+        try:
+            user = User.query.filter_by(id=data.user_id).first()
+            shared_server = SharedServer(
+                user_id=current_user.id,
+                server_owner=user.username,
+                servergroup_id=gid,
+                name=data.name,
+                host=data.host,
+                hostaddr=data.hostaddr,
+                port=data.port,
+                maintenance_db=None,
+                username=None,
+                save_password=0,
+                ssl_mode=data.ssl_mode,
+                comment=None,
+                role=data.role,
+                sslcert=None,
+                sslkey=None,
+                sslrootcert=None,
+                sslcrl=None,
+                bgcolor=data.bgcolor if data.bgcolor else None,
+                fgcolor=data.fgcolor if data.fgcolor else None,
+                service=data.service if data.service else None,
+                connect_timeout=0,
+                use_ssh_tunnel=0,
+                tunnel_host=None,
+                tunnel_port=22,
+                tunnel_username=None,
+                tunnel_authentication=0,
+                tunnel_identity_file=None,
+                shared=data.shared if data.shared else None
+            )
+            db.session.add(shared_server)
+            db.session.commit()
+        except Exception as e:
+            if shared_server:
+                db.session.delete(shared_server)
+                db.session.commit()
+
+            current_app.logger.exception(e)
+            return internal_server_error(errormsg=str(e))
+
+    @staticmethod
+    def get_shared_server(server, gid):
+        """
+        return the shared server
+        :param server:
+        :param gid:
+        :return: shared_server
+        """
+        auto_detected_server = None
+        shared_server = SharedServer.query.filter_by(
+            name=server.name, user_id=current_user.id,
+            servergroup_id=gid).first()
+        if server.discovery_id:
+            auto_detected_server = server.name
+
+        if shared_server is None:
+            ServerModule.create_shared_server(server, gid)
+
+            shared_server = SharedServer.query.filter_by(
+                name=server.name, user_id=current_user.id,
+                servergroup_id=gid).first()
+
+        return shared_server, auto_detected_server
+
 
 class ServerMenuItem(MenuItem):
     def __init__(self, **kwargs):
@@ -327,19 +493,29 @@ class ServerNode(PGChildNodeView):
         Return a JSON document listing the servers under this server group
         for the user.
         """
-        servers = Server.query.filter_by(user_id=current_user.id,
-                                         servergroup_id=gid)
+        servers = Server.query.filter(
+            or_(Server.user_id == current_user.id,
+                Server.shared),
+            Server.servergroup_id == gid)
 
         driver = get_driver(PG_DEFAULT_DRIVER)
 
         for server in servers:
+            if server.shared and server.user_id != current_user.id:
+                shared_server, auto_detected_server = \
+                    ServerModule.get_shared_server(server, gid)
+                server = \
+                    ServerModule.get_shared_server_properties(server,
+                                                              shared_server)
             manager = driver.connection_manager(server.id)
             conn = manager.connection()
             connected = conn.connected()
             errmsg = None
             in_recovery = None
             wal_paused = None
+            server_type = 'pg'
             if connected:
+                server_type = manager.server_type
                 status, result, in_recovery, wal_paused =\
                     recovery_state(conn, manager.version)
                 if not status:
@@ -356,7 +532,7 @@ class ServerNode(PGChildNodeView):
                     True,
                     self.node_type,
                     connected=connected,
-                    server_type=manager.server_type if connected else 'pg',
+                    server_type=server_type,
                     version=manager.version,
                     db=manager.db,
                     user=manager.user_info if connected else None,
@@ -365,7 +541,9 @@ class ServerNode(PGChildNodeView):
                     is_password_saved=bool(server.save_password),
                     is_tunnel_password_saved=True
                     if server.tunnel_password is not None else False,
-                    errmsg=errmsg
+                    errmsg=errmsg,
+                    user_name=server.username,
+                    shared=server.shared
                 )
             )
 
@@ -379,9 +557,13 @@ class ServerNode(PGChildNodeView):
     @login_required
     def node(self, gid, sid):
         """Return a JSON document listing the server groups for the user"""
-        server = Server.query.filter_by(user_id=current_user.id,
-                                        servergroup_id=gid,
-                                        id=sid).first()
+        server = Server.query.filter_by(id=sid).first()
+
+        if server.shared and server.user_id != current_user.id:
+            shared_server, auto_detected_server = \
+                ServerModule.get_shared_server(server, gid)
+            server = ServerModule.get_shared_server_properties(server,
+                                                               shared_server)
 
         if server is None:
             return make_json_response(
@@ -426,14 +608,37 @@ class ServerNode(PGChildNodeView):
                 is_password_saved=bool(server.save_password),
                 is_tunnel_password_saved=True
                 if server.tunnel_password is not None else False,
-                errmsg=errmsg
+                errmsg=errmsg,
+                shared=server.shared,
+                user_name=server.username
             ),
         )
 
+    def delete_shared_server(self, server_name, gid):
+        """
+        Delete the shared server
+        :param server_name:
+        :return:
+        """
+        try:
+            shared_server = SharedServer.query.filter_by(name=server_name,
+                                                         servergroup_id=gid)
+            for s in shared_server:
+                get_driver(PG_DEFAULT_DRIVER).delete_manager(s.id)
+                db.session.delete(s)
+            db.session.commit()
+
+        except Exception as e:
+            current_app.logger.exception(e)
+            return make_json_response(
+                success=0,
+                errormsg=e.message)
+
     @login_required
     def delete(self, gid, sid):
         """Delete a server node in the settings database."""
         servers = Server.query.filter_by(user_id=current_user.id, id=sid)
+        server_name = None
 
         # TODO:: A server, which is connected, cannot be deleted
         if servers is None:
@@ -449,10 +654,11 @@ class ServerNode(PGChildNodeView):
         else:
             try:
                 for s in servers:
+                    server_name = s.name
                     get_driver(PG_DEFAULT_DRIVER).delete_manager(s.id)
                     db.session.delete(s)
                 db.session.commit()
-
+                self.delete_shared_server(server_name, gid)
                 QueryHistory.clear_history(current_user.id, sid)
 
             except Exception as e:
@@ -467,8 +673,8 @@ class ServerNode(PGChildNodeView):
     @login_required
     def update(self, gid, sid):
         """Update the server settings"""
-        server = Server.query.filter_by(
-            user_id=current_user.id, id=sid).first()
+        server = Server.query.filter_by(id=sid).first()
+        sharedserver = None
 
         if server is None:
             return make_json_response(
@@ -477,6 +683,11 @@ class ServerNode(PGChildNodeView):
                 errormsg=gettext("Could not find the required server.")
             )
 
+        if config.SERVER_MODE and server.shared and \
+                server.user_id != current_user.id:
+            sharedserver, auto_detected_server = \
+                ServerModule.get_shared_server(server, gid)
+
         # Not all parameters can be modified, while the server is connected
         config_param_map = {
             'name': 'name',
@@ -506,11 +717,12 @@ class ServerNode(PGChildNodeView):
             'tunnel_username': 'tunnel_username',
             'tunnel_authentication': 'tunnel_authentication',
             'tunnel_identity_file': 'tunnel_identity_file',
+            'shared': 'shared'
         }
 
         disp_lbl = {
             'name': gettext('name'),
-            'host': gettext('Host name/address'),
+            'hostaddr': gettext('Host name/address'),
             'port': gettext('Port'),
             'db': gettext('Maintenance database'),
             'username': gettext('Username'),
@@ -541,7 +753,8 @@ class ServerNode(PGChildNodeView):
         self._server_modify_disallowed_when_connected(
             connected, data, disp_lbl)
 
-        idx = self._set_valid_attr_value(data, config_param_map, server)
+        idx = self._set_valid_attr_value(gid, data, config_param_map, server,
+                                         sharedserver)
 
         if idx == 0:
             return make_json_response(
@@ -568,7 +781,11 @@ class ServerNode(PGChildNodeView):
             node=self.blueprint.generate_browser_node(
                 "%d" % (server.id), server.servergroup_id,
                 server.name,
-                server_icon_and_background(connected, manager, server),
+                server_icon_and_background(
+                    connected, manager, sharedserver)
+                if server.shared and server.user_id != current_user.id
+                else server_icon_and_background(
+                    connected, manager, server),
                 True,
                 self.node_type,
                 connected=connected,
@@ -577,7 +794,16 @@ class ServerNode(PGChildNodeView):
             )
         )
 
-    def _set_valid_attr_value(self, data, config_param_map, server):
+    @staticmethod
+    def _update_server_details(server, sharedserver,
+                               config_param_map, arg, value):
+        if server.shared and server.user_id != current_user.id:
+            setattr(sharedserver, config_param_map[arg], value)
+        else:
+            setattr(server, config_param_map[arg], value)
+
+    def _set_valid_attr_value(self, gid, data, config_param_map, server,
+                              sharedserver):
 
         idx = 0
         for arg in config_param_map:
@@ -585,9 +811,14 @@ class ServerNode(PGChildNodeView):
                 value = data[arg]
                 # sqlite3 do not have boolean type so we need to convert
                 # it manually to integer
+                if 'shared' in data and not data['shared']:
+                    # Delete the shared server from DB if server
+                    # owner uncheck shared property
+                    self.delete_shared_server(server.name, gid)
                 if arg == 'sslcompression':
                     value = 1 if value else 0
-                setattr(server, config_param_map[arg], value)
+                self._update_server_details(server, sharedserver,
+                                            config_param_map, arg, value)
                 idx += 1
 
         return idx
@@ -613,11 +844,11 @@ class ServerNode(PGChildNodeView):
         """
         Return list of attributes of all servers.
         """
-        servers = Server.query.filter_by(
-            user_id=current_user.id,
-            servergroup_id=gid).order_by(Server.name)
+        servers = Server.query.filter(
+            or_(Server.user_id == current_user.id,
+                Server.shared),
+            Server.servergroup_id == gid).order_by(Server.name)
         sg = ServerGroup.query.filter_by(
-            user_id=current_user.id,
             id=gid
         ).first()
         res = []
@@ -625,6 +856,12 @@ class ServerNode(PGChildNodeView):
         driver = get_driver(PG_DEFAULT_DRIVER)
 
         for server in servers:
+            if server.shared and server.user_id != current_user.id:
+                shared_server, auto_detected_server = \
+                    ServerModule.get_shared_server(server, gid)
+                server = \
+                    ServerModule.get_shared_server_properties(server,
+                                                              shared_server)
             manager = driver.connection_manager(server.id)
             conn = manager.connection()
             connected = conn.connected()
@@ -653,8 +890,12 @@ class ServerNode(PGChildNodeView):
     @login_required
     def properties(self, gid, sid):
         """Return list of attributes of a server"""
+
+        sslcert = None
+        sslkey = None
+        sslrootcert = None
+        sslcrl = None
         server = Server.query.filter_by(
-            user_id=current_user.id,
             id=sid).first()
 
         if server is None:
@@ -663,9 +904,8 @@ class ServerNode(PGChildNodeView):
                 success=0,
                 errormsg=self.not_found_error_msg()
             )
-
+        server_owner = None
         sg = ServerGroup.query.filter_by(
-            user_id=current_user.id,
             id=server.servergroup_id
         ).first()
 
@@ -675,52 +915,77 @@ class ServerNode(PGChildNodeView):
         conn = manager.connection()
         connected = conn.connected()
 
+        # if server.shared and not current_user.has_role("Administrator"):
+        if server.shared and server.user_id != current_user.id:
+            shared_server, auto_detected_server = \
+                ServerModule.get_shared_server(server, gid)
+            server = ServerModule.get_shared_server_properties(server,
+                                                               shared_server)
+            server_owner = server.server_owner
+
         is_ssl = True if server.ssl_mode in self.SSL_MODES else False
 
-        return ajax_response(
-            response={
-                'id': server.id,
-                'name': server.name,
-                'host': server.host,
-                'hostaddr': server.hostaddr,
-                'port': server.port,
-                'db': server.maintenance_db,
-                'username': server.username,
-                'gid': str(server.servergroup_id),
-                'group-name': sg.name,
-                'comment': server.comment,
-                'role': server.role,
-                'connected': connected,
-                'version': manager.ver,
-                'sslmode': server.ssl_mode,
-                'server_type': manager.server_type if connected else 'pg',
-                'bgcolor': server.bgcolor,
-                'fgcolor': server.fgcolor,
-                'db_res': server.db_res.split(',') if server.db_res else None,
-                'passfile': server.passfile if server.passfile else None,
-                'sslcert': server.sslcert if is_ssl else None,
-                'sslkey': server.sslkey if is_ssl else None,
-                'sslrootcert': server.sslrootcert if is_ssl else None,
-                'sslcrl': server.sslcrl if is_ssl else None,
-                'sslcompression': True if is_ssl and server.sslcompression
-                else False,
-                'service': server.service if server.service else None,
-                'connect_timeout':
-                    server.connect_timeout if server.connect_timeout else 0,
-                'use_ssh_tunnel': server.use_ssh_tunnel
-                if server.use_ssh_tunnel else 0,
-                'tunnel_host': server.tunnel_host if server.tunnel_host
-                else None,
-                'tunnel_port': server.tunnel_port if server.tunnel_port
-                else 22,
-                'tunnel_username': server.tunnel_username
-                if server.tunnel_username else None,
-                'tunnel_identity_file': server.tunnel_identity_file
-                if server.tunnel_identity_file else None,
-                'tunnel_authentication': server.tunnel_authentication
-                if server.tunnel_authentication else 0
-            }
-        )
+        if is_ssl:
+            sslcert = server.sslcert
+            sslkey = server.sslkey
+            sslrootcert = server.sslrootcert
+            sslcrl = server.sslcrl
+
+        use_ssh_tunnel = 0
+        tunnel_host = None
+        tunnel_port = 22
+        tunnel_username = None
+        tunnel_authentication = 0
+
+        if server.use_ssh_tunnel:
+            use_ssh_tunnel = server.use_ssh_tunnel
+            tunnel_host = server.tunnel_host
+            tunnel_port = server.tunnel_port
+            tunnel_username = server.tunnel_username
+            tunnel_authentication = server.tunnel_authentication
+
+        response = {
+            'id': server.id,
+            'name': server.name,
+            'server_owner': server_owner,
+            'user_id': server.user_id,
+            'host': server.host,
+            'hostaddr': server.hostaddr,
+            'port': server.port,
+            'db': server.maintenance_db,
+            'shared': server.shared if config.SERVER_MODE else None,
+            'username': server.username,
+            'gid': str(server.servergroup_id),
+            'group-name': sg.name,
+            'comment': server.comment,
+            'role': server.role,
+            'connected': connected,
+            'version': manager.ver,
+            'sslmode': server.ssl_mode,
+            'server_type': manager.server_type if connected else 'pg',
+            'bgcolor': server.bgcolor,
+            'fgcolor': server.fgcolor,
+            'db_res': server.db_res.split(',') if server.db_res else None,
+            'passfile': server.passfile if server.passfile else None,
+            'sslcert': sslcert,
+            'sslkey': sslkey,
+            'sslrootcert': sslrootcert,
+            'sslcrl': sslcrl,
+            'sslcompression': True if is_ssl and server.sslcompression
+            else False,
+            'service': server.service if server.service else None,
+            'connect_timeout':
+                server.connect_timeout if server.connect_timeout else 0,
+            'use_ssh_tunnel': use_ssh_tunnel,
+            'tunnel_host': tunnel_host,
+            'tunnel_port': tunnel_port,
+            'tunnel_username': tunnel_username,
+            'tunnel_identity_file': server.tunnel_identity_file
+            if server.tunnel_identity_file else None,
+            'tunnel_authentication': tunnel_authentication
+        }
+
+        return ajax_response(response)
 
     @login_required
     def create(self, gid):
@@ -802,11 +1067,11 @@ class ServerNode(PGChildNodeView):
                 tunnel_port=data.get('tunnel_port', 22),
                 tunnel_username=data.get('tunnel_username', None),
                 tunnel_authentication=data.get('tunnel_authentication', 0),
-                tunnel_identity_file=data.get('tunnel_identity_file', None)
+                tunnel_identity_file=data.get('tunnel_identity_file', None),
+                shared=data.get('shared', None)
             )
             db.session.add(server)
             db.session.commit()
-
             connected = False
             user = None
             manager = None
@@ -1006,9 +1271,23 @@ class ServerNode(PGChildNodeView):
 
         # Fetch Server Details
         server = Server.query.filter_by(id=sid).first()
+        shared_server = None
+        if server.shared and server.user_id != current_user.id:
+            shared_server, auto_detected_server = \
+                ServerModule.get_shared_server(server, gid)
+            server = ServerModule.get_shared_server_properties(server,
+                                                               shared_server)
         if server is None:
             return bad_request(self.not_found_error_msg())
 
+        # Return if username is blank
+        if server.username is None:
+            return make_json_response(
+                status=200,
+                success=0,
+                errormsg=gettext(
+                    u"Please enter the server details to connect")
+            )
         if current_user and hasattr(current_user, 'id'):
             # Fetch User Details.
             user = User.query.filter_by(id=current_user.id).first()
@@ -1063,7 +1342,6 @@ class ServerNode(PGChildNodeView):
                 except Exception as e:
                     current_app.logger.exception(e)
                     return internal_server_error(errormsg=str(e))
-
         if 'password' not in data:
             conn_passwd = getattr(conn, 'password', None)
             if conn_passwd is None and not server.save_password and \
@@ -1125,10 +1403,18 @@ class ServerNode(PGChildNodeView):
                     # every time user try to connect
                     # 1 is True in SQLite as no boolean type
                     setattr(server, 'save_password', 1)
+                    if server.shared and server.user_id != current_user.id:
+                        setattr(shared_server, 'save_password', 1)
+                    else:
+                        setattr(server, 'save_password', 1)
+
                     # Save the encrypted password using the user's login
                     # password key, if there is any password to save
                     if password:
-                        setattr(server, 'password', password)
+                        if server.shared and server.user_id != current_user.id:
+                            setattr(shared_server, 'password', password)
+                        else:
+                            setattr(server, 'password', password)
                     db.session.commit()
                 except Exception as e:
                     # Release Connection
@@ -1560,21 +1846,39 @@ class ServerNode(PGChildNodeView):
         :return:
         """
         try:
-            server = Server.query.filter_by(
-                user_id=current_user.id, id=sid
-            ).first()
-
+            server = Server.query.filter_by(id=sid).first()
+            shared_server = None
             if server is None:
                 return make_json_response(
                     success=0,
                     info=self.not_found_error_msg()
                 )
 
-            setattr(server, 'password', None)
+            if server.shared and server.user_id != current_user.id:
+                shared_server = SharedServer.query.filter_by(
+                    name=server.name, user_id=current_user.id,
+                    servergroup_id=gid).first()
+
+                if shared_server is None:
+                    return make_json_response(
+                        success=0,
+                        info=gettext("Could not find the required server.")
+                    )
+                server = ServerModule. \
+                    get_shared_server_properties(server, shared_server)
+
+            if server.shared and server.user_id != current_user.id:
+                setattr(shared_server, 'save_password', None)
+            else:
+                setattr(server, 'save_password', None)
+
             # If password was saved then clear the flag also
             # 0 is False in SQLite db
             if server.save_password:
-                setattr(server, 'save_password', 0)
+                if server.shared and server.user_id != current_user.id:
+                    setattr(shared_server, 'save_password', 0)
+                else:
+                    setattr(server, 'save_password', 0)
             db.session.commit()
         except Exception as e:
             current_app.logger.error(
@@ -1599,10 +1903,7 @@ class ServerNode(PGChildNodeView):
         :return:
         """
         try:
-            server = Server.query.filter_by(
-                user_id=current_user.id, id=sid
-            ).first()
-
+            server = Server.query.filter_by(id=sid).first()
             if server is None:
                 return make_json_response(
                     success=0,
diff --git a/web/pgadmin/browser/server_groups/servers/static/img/sharedserverbad.svg b/web/pgadmin/browser/server_groups/servers/static/img/sharedserverbad.svg
new file mode 100644
index 000000000..4455089ac
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/static/img/sharedserverbad.svg
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.2.2, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#F2F2F2;}
+	.st1{fill:#9BA5B0;}
+	.st2{fill:none;stroke:#9BA5B0;stroke-width:0.75;stroke-miterlimit:1;}
+	.st3{fill:#F7F7F7;}
+	.st4{fill:#354A5F;}
+	.st5{fill:none;stroke:#D0021B;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;}
+</style>
+<g>
+	<path class="st0" d="M6.7,2.1h4.6c0.6,0,1.1,0.5,1.1,1.1v0.8c0,0.6-0.5,1.1-1.1,1.1H6.7c-0.6,0-1.1-0.5-1.1-1.1V3.2
+		C5.6,2.6,6.1,2.1,6.7,2.1z"/>
+	<path class="st1" d="M11.3,2.5c0.4,0,0.8,0.3,0.8,0.8v0.8c0,0.4-0.3,0.8-0.8,0.8H6.7C6.3,4.8,6,4.5,6,4.1V3.2
+		c0-0.4,0.3-0.7,0.8-0.7H11.3 M11.3,1.7H6.7c-0.8,0-1.5,0.7-1.5,1.5v0.8c0,0.8,0.7,1.5,1.5,1.5h4.6c0.8,0,1.5-0.7,1.5-1.5V3.2
+		C12.8,2.4,12.1,1.7,11.3,1.7L11.3,1.7z"/>
+	<line class="st2" x1="8.9" y1="3.6" x2="6.8" y2="3.6"/>
+	<path class="st0" d="M6.7,5.2h4.6c0.6,0,1.1,0.5,1.1,1.1v0.8c0,0.6-0.5,1.1-1.1,1.1H6.7c-0.6,0-1.1-0.5-1.1-1.1V6.3
+		C5.6,5.7,6.1,5.2,6.7,5.2z"/>
+	<path class="st1" d="M11.3,5.6c0.4,0,0.8,0.3,0.8,0.8v0.8c0,0.4-0.3,0.8-0.8,0.8H6.7C6.3,7.9,6,7.6,6,7.1V6.3
+		c0-0.4,0.3-0.7,0.8-0.7H11.3 M11.3,4.8H6.7c-0.8,0-1.5,0.7-1.5,1.5v0.8c0,0.8,0.7,1.5,1.5,1.5h4.6c0.8,0,1.5-0.7,1.5-1.5V6.3
+		C12.8,5.5,12.1,4.8,11.3,4.8L11.3,4.8z"/>
+	<line class="st2" x1="8.9" y1="6.7" x2="6.8" y2="6.7"/>
+	<path class="st0" d="M6.7,8.3h4.6c0.6,0,1.1,0.5,1.1,1.1v0.9c0,0.6-0.5,1.1-1.1,1.1c0,0,0,0,0,0H6.7c-0.6,0-1.1-0.5-1.1-1.1l0,0
+		V9.4C5.6,8.8,6.1,8.3,6.7,8.3C6.7,8.3,6.7,8.3,6.7,8.3z"/>
+	<path class="st1" d="M11.3,8.7c0.4,0,0.8,0.3,0.8,0.8v0.8c0,0.4-0.3,0.8-0.8,0.8H6.7C6.3,11,6,10.7,6,10.3V9.4C6,9,6.3,8.7,6.7,8.7
+		H11.3 M11.3,7.9H6.7c-0.8,0-1.5,0.7-1.5,1.5v0.8c0,0.8,0.7,1.5,1.5,1.5h4.6c0.8,0,1.5-0.7,1.5-1.5V9.4C12.8,8.6,12.1,7.9,11.3,7.9z
+		"/>
+	<line class="st2" x1="8.9" y1="9.8" x2="6.8" y2="9.8"/>
+</g>
+<path class="st3" d="M11.6,10.5c-0.6,0.3-0.8,0.1-1.1-0.2c-0.1-0.2-0.3-0.4-0.6-0.5L7.1,8.5C6.8,8.4,6.4,8.3,6.1,8.3
+	C5.1,8.1,4.2,8.4,3.4,9l-1.4,1.1v2.7h0.9v-0.2l3.7,1.8c0.4,0.2,0.7,0.3,1.1,0.3c0.3,0,0.7-0.1,1-0.2l6.1-2.6
+	c0.3-0.1,0.6-0.4,0.7-0.7c0.1-0.3,0.1-0.7,0-1c-0.3-0.6-1.1-0.9-1.7-0.6l-0.4,0.2L11.6,10.5"/>
+<path class="st4" d="M15.5,10.2c-0.3-0.6-1.1-0.9-1.7-0.6l-0.4,0.2l-1.8,0.8l0.3,0.8l1.8-0.8l0.4-0.2c0.2-0.1,0.5,0,0.6,0.2
+	c0.1,0.1,0.1,0.2,0,0.3c0,0.1-0.1,0.2-0.2,0.2l-6.1,2.6c-0.5,0.2-1,0.2-1.4,0l-4.1-2v-1.2l1-0.8C4.5,9.2,5.2,9,6,9.1
+	c0.3,0,0.6,0.1,0.8,0.2l2.8,1.2c0.1,0,0.1,0.1,0.2,0.2c0.1,0.1,0.1,0.3,0,0.4c0,0,0,0.1-0.1,0.1c-0.1,0.1-0.3,0.2-0.5,0.1l-2.2-1
+	l-0.4,0.8l2.2,1c0.2,0.1,0.4,0.1,0.5,0.1c0.4,0,0.7-0.2,1-0.4c0.1-0.1,0.2-0.2,0.2-0.3c0.2-0.4,0.1-0.9-0.1-1.2
+	c-0.1-0.2-0.3-0.4-0.6-0.5L7.1,8.5C6.8,8.4,6.4,8.3,6.1,8.3C5.1,8.1,4.2,8.4,3.4,9L2.9,9.4V9.3c0-0.6-0.4-1-1-1H1.4
+	c-0.6,0-1,0.4-1,1v4.1c0,0.6,0.4,1,1,1h0.5c0.6,0,1-0.4,1-1v-0.6h0v-0.2l3.7,1.8c0.4,0.2,0.7,0.3,1.1,0.3c0.3,0,0.7-0.1,1-0.2
+	l6.1-2.6c0.3-0.1,0.6-0.4,0.7-0.7C15.7,10.9,15.7,10.5,15.5,10.2z M1.9,13.4H1.4V9.3h0.5L1.9,13.4z"/>
+<line class="st5" x1="13.8" y1="1.3" x2="10.8" y2="4.3"/>
+<line class="st5" x1="13.8" y1="4.3" x2="10.8" y2="1.3"/>
+</svg>
diff --git a/web/pgadmin/browser/server_groups/servers/static/js/server.js b/web/pgadmin/browser/server_groups/servers/static/js/server.js
index 8c43a55f7..9170e0d44 100644
--- a/web/pgadmin/browser/server_groups/servers/static/js/server.js
+++ b/web/pgadmin/browser/server_groups/servers/static/js/server.js
@@ -56,7 +56,12 @@ define('pgadmin.node.server', [
       type: 'server',
       dialogHelp: url_for('help.static', {'filename': 'server_dialog.html'}),
       label: gettext('Server'),
-      canDrop: true,
+      canDrop: function(node){
+        var serverOwner = node.user_id;
+        if (serverOwner != current_user.id)
+          return false;
+        return true;
+      },
       dropAsRemove: true,
       dropPriority: 5,
       hasStatistics: true,
@@ -75,12 +80,12 @@ define('pgadmin.node.server', [
           name: 'create_server_on_sg', node: 'server_group', module: this,
           applies: ['object', 'context'], callback: 'show_obj_properties',
           category: 'create', priority: 1, label: gettext('Server...'),
-          data: {action: 'create'}, icon: 'wcTabIcon icon-server',
+          data: {action: 'create'}, icon: 'wcTabIcon icon-server', enable: 'canCreate',
         },{
           name: 'create_server', node: 'server', module: this,
           applies: ['object', 'context'], callback: 'show_obj_properties',
           category: 'create', priority: 3, label: gettext('Server...'),
-          data: {action: 'create'}, icon: 'wcTabIcon icon-server',
+          data: {action: 'create'}, icon: 'wcTabIcon icon-server', enable: 'canCreate',
         },{
           name: 'connect_server', node: 'server', module: this,
           applies: ['object', 'context'], callback: 'connect_server',
@@ -150,6 +155,13 @@ define('pgadmin.node.server', [
       is_not_connected: function(node) {
         return (node && node.connected != true);
       },
+      canCreate: function(node){
+        var serverOwner = node.user_id;
+        if (serverOwner == current_user.id || _.isUndefined(serverOwner))
+          return true;
+        return false;
+
+      },
       is_connected: function(node) {
         return (node && node.connected == true);
       },
@@ -226,28 +238,25 @@ define('pgadmin.node.server', [
                     d = t.itemData(i);
                     t.removeIcon(i);
                     d.connected = false;
-                    d.icon = 'icon-server-not-connected';
+                    if (d.shared){
+                      d.icon = 'icon-shared-server-not-connected';
+                    }else{
+                      d.icon = 'icon-server-not-connected';
+                    }
                     t.addIcon(i, {icon: d.icon});
                     obj.callbacks.refresh.apply(obj, [null, i]);
                     if (pgBrowser.serverInfo && d._id in pgBrowser.serverInfo) {
                       delete pgBrowser.serverInfo[d._id];
                     }
-                    pgBrowser.enable_disable_menus(i);
-                    // Trigger server disconnect event
-                    pgBrowser.Events.trigger(
-                      'pgadmin:server:disconnect',
-                      {item: i, data: d}, false
-                    );
-                  }
-                  else {
-                    try {
-                      Alertify.error(res.errormsg);
-                    } catch (e) {
-                      console.warn(e.stack || e);
+                    else {
+                      try {
+                        Alertify.error(res.errormsg);
+                      } catch (e) {
+                        console.warn(e.stack || e);
+                      }
+                      t.unload(i);
                     }
-                    t.unload(i);
-                  }
-                })
+                  }})
                 .fail(function(xhr, status, error) {
                   Alertify.pgRespErrorNotify(xhr, error);
                   t.unload(i);
@@ -735,6 +744,7 @@ define('pgadmin.node.server', [
         // Default values!
         initialize: function(attrs, args) {
           var isNew = (_.size(attrs) === 0);
+          console.warn('warn');
 
           if (isNew) {
             this.set({'gid': args.node_info['server_group']._id});
@@ -745,12 +755,17 @@ define('pgadmin.node.server', [
           id: 'id', label: gettext('ID'), type: 'int', mode: ['properties'],
         },{
           id: 'name', label: gettext('Name'), type: 'text',
-          mode: ['properties', 'edit', 'create'],
-        },{
+          mode: ['properties', 'edit', 'create'], disabled: 'isShared',
+        },
+        {
           id: 'gid', label: gettext('Server group'), type: 'int',
           control: 'node-list-by-id', node: 'server_group',
-          mode: ['create', 'edit'], select2: {allowClear: false},
-        },{
+          mode: ['create', 'edit'], select2: {allowClear: false}, visible: 'isVisible',
+        },
+        {
+          id: 'server_owner', label: gettext('Shared Server Owner'), type: 'text', mode: ['properties'],
+        },
+        {
           id: 'server_type', label: gettext('Server type'), type: 'options',
           mode: ['properties'], visible: 'isConnected',
           'options': supported_servers,
@@ -773,11 +788,27 @@ define('pgadmin.node.server', [
           id: 'connect_now', controlLabel: gettext('Connect now?'), type: 'checkbox',
           group: null, mode: ['create'],
         },{
+          id: 'shared', label: gettext('Shared with all?'), type: 'switch',
+          mode: ['properties', 'create', 'edit'], 'options': {'size': 'mini'},
+          readonly: function(model){
+            var serverOwner = model.attributes.user_id;
+            if (!model.isNew() && serverOwner != current_user.id){
+              return true;
+            }
+            return false;
+          },visible: function(){
+            if (current_user.is_admin && pgAdmin.server_mode == 'True')
+              return true;
+
+            return false;
+          },
+        },
+        {
           id: 'comment', label: gettext('Comments'), type: 'multiline', group: null,
           mode: ['properties', 'edit', 'create'],
         },{
           id: 'host', label: gettext('Host name/address'), type: 'text', group: gettext('Connection'),
-          mode: ['properties', 'edit', 'create'],
+          mode: ['properties', 'edit', 'create'],disabled: 'isShared',
           control: Backform.InputControl.extend({
             onChange: function() {
               Backform.InputControl.prototype.onChange.apply(this, arguments);
@@ -798,7 +829,7 @@ define('pgadmin.node.server', [
           }),
         },{
           id: 'port', label: gettext('Port'), type: 'int', group: gettext('Connection'),
-          mode: ['properties', 'edit', 'create'], min: 1, max: 65535,
+          mode: ['properties', 'edit', 'create'], min: 1, max: 65535, disabled: 'isShared',
           control: Backform.InputControl.extend({
             onChange: function() {
               Backform.InputControl.prototype.onChange.apply(this, arguments);
@@ -819,7 +850,7 @@ define('pgadmin.node.server', [
           }),
         },{
           id: 'db', label: gettext('Maintenance database'), type: 'text', group: gettext('Connection'),
-          mode: ['properties', 'edit', 'create'], readonly: 'isConnected',
+          mode: ['properties', 'edit', 'create'], readonly: 'isConnected',disabled: 'isShared',
         },{
           id: 'username', label: gettext('Username'), type: 'text', group: gettext('Connection'),
           mode: ['properties', 'edit', 'create'],
@@ -1057,6 +1088,21 @@ define('pgadmin.node.server', [
           mode: ['properties', 'edit', 'create'], readonly: 'isConnected',
           min: 0,
         }],
+        isVisible: function(model){
+          var serverOwner = model.attributes.user_id;
+          if (!model.isNew() && serverOwner != current_user.id){
+            return false;
+          }
+          return true;
+
+        },
+        isShared: function(model){
+          var serverOwner = model.attributes.user_id;
+          if (!model.isNew() && serverOwner != current_user.id && model.attributes.shared){
+            return true;
+          }
+          return false;
+        },
         validate: function() {
           const validateModel = new modelValidation.ModelValidation(this);
           return validateModel.validate();
@@ -1153,7 +1199,36 @@ define('pgadmin.node.server', [
         }
       },
     });
+
     var connect_to_server = function(obj, data, tree, item, reconnect) {
+    // Open properties dialog in edit mode
+      const selectedTreeNode = tree.selected().length > 0 ? tree.selected() : tree.first();
+      const selectedTreeNodeData = selectedTreeNode && selectedTreeNode.length === 1 ? tree.itemData(selectedTreeNode) : undefined;
+      var server_url = obj.generate_url(item, 'obj', data, true);
+      // Fetch the updated data
+      $.get(server_url)
+        .done(function(res) {
+          if (res.shared && _.isNull(res.username) && data.user_id != current_user.id){
+            if (selectedTreeNodeData._type == 'server'){
+              pgAdmin.Browser.Node.callbacks.show_obj_properties.call(
+                pgAdmin.Browser.Nodes[tree.itemData(item)._type], {action: 'edit'}
+              );
+              data.is_connecting = false;
+              tree.unload(item);
+              tree.setInode(item);
+              tree.addIcon(item, {icon: 'icon-shared-server-not-connected'});
+            }else{
+              data.is_connecting = false;
+              tree.unload(item);
+              tree.setInode(item);
+              tree.addIcon(item, {icon: 'icon-shared-server-not-connected'});
+            }
+          }
+          return;
+        }).always(function(){
+          data.is_connecting = false;
+        });
+
       var wasConnected = reconnect || data.connected,
         onFailure = function(
           xhr, status, error, _node, _data, _tree, _item, _wasConnected
@@ -1164,7 +1239,12 @@ define('pgadmin.node.server', [
           // Let's not change the status of the tree node now.
           if (!_wasConnected) {
             tree.setInode(_item);
-            tree.addIcon(_item, {icon: 'icon-server-not-connected'});
+            if (data.shared){
+              tree.addIcon(_item, {icon: 'icon-shared-server-not-connected'});
+            }else{
+              tree.addIcon(_item, {icon: 'icon-server-not-connected'});
+            }
+
           }
 
           Alertify.pgNotifier('error', xhr, error, function(msg) {
@@ -1321,7 +1401,11 @@ define('pgadmin.node.server', [
         _tree.unload(_item);
         _tree.setInode(_item);
         _tree.removeIcon(_item);
-        _tree.addIcon(_item, {icon: 'icon-server-not-connected'});
+        if (_data.shared){
+          _tree.addIcon(_item, {icon: 'icon-shared-server-not-connected'});
+        }else{
+          _tree.addIcon(_item, {icon: 'icon-server-not-connected'});
+        }
         obj.trigger('connect:cancelled', data._id, data.db, obj, _item, _data);
         pgBrowser.Events.trigger(
           'pgadmin:server:connect:cancelled', data._id, _item, _data, obj
@@ -1387,7 +1471,11 @@ define('pgadmin.node.server', [
         })
         .fail(function(xhr, status, error) {
           tree.setInode(item);
-          tree.addIcon(item, {icon: 'icon-server-not-connected'});
+          if (data.shared){
+            tree.addIcon(item, {icon: 'icon-shared-server-not-connected'});
+          }else{
+            tree.addIcon(item, {icon: 'icon-server-not-connected'});
+          }
           Alertify.pgRespErrorNotify(xhr, error);
         });
     };
diff --git a/web/pgadmin/browser/server_groups/servers/templates/css/servers.css b/web/pgadmin/browser/server_groups/servers/templates/css/servers.css
index 93c4536d4..829c273aa 100644
--- a/web/pgadmin/browser/server_groups/servers/templates/css/servers.css
+++ b/web/pgadmin/browser/server_groups/servers/templates/css/servers.css
@@ -15,3 +15,21 @@
   vertical-align: middle;
   height: 1.3em;
 }
+
+.icon-shared-server-not-connected {
+  background-image: url('{{ url_for('NODE-server.static', filename='img/sharedserverbad.svg') }}') !important;
+  background-repeat: no-repeat;
+  background-size: 20px !important;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
+
+.icon-shared-server-not-connected {
+  background-image: url('{{ url_for('NODE-server.static', filename='img/sharedserverbad.svg') }}') !important;
+  background-repeat: no-repeat;
+  background-size: 20px !important;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
diff --git a/web/pgadmin/browser/server_groups/servers/tests/servers_test_data.json b/web/pgadmin/browser/server_groups/servers/tests/servers_test_data.json
new file mode 100644
index 000000000..117a2982b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tests/servers_test_data.json
@@ -0,0 +1,862 @@
+{
+  "add_server": [
+    {
+      "name": "Add server with service id",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "owner_server": true,
+      "test_data": {
+        "service": "TestDB"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Test default server url",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "owner_server": true,
+      "test_data": {
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Add server with connect timeout",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "test_data": {
+        "connect_timeout": 5
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Add server using SSH tunnel with password",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "ssh_tunnel": true,
+      "with_password": true,
+      "save_password": false,
+      "test_data": {
+        "use_ssh_tunnel": 1,
+        "tunnel_host": "127.0.0.1",
+        "tunnel_port": 22,
+        "tunnel_username": "user",
+        "tunnel_authentication": 1,
+        "tunnel_identity_file": "pkey_rsa"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Add server using SSH tunnel with identity file",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "ssh_tunnel": true,
+      "with_password": false,
+      "save_password": false,
+      "test_data": {
+        "use_ssh_tunnel": 1,
+        "tunnel_host": "127.0.0.1",
+        "tunnel_port": 22,
+        "tunnel_username": "user",
+        "tunnel_authentication": 1,
+        "tunnel_identity_file": "pkey_rsa"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Add server using SSH tunnel with password and saved it",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "ssh_tunnel": true,
+      "with_password": true,
+      "save_password": true,
+      "test_data": {
+        "use_ssh_tunnel": 1,
+        "tunnel_host": "127.0.0.1",
+        "tunnel_port": 22,
+        "tunnel_username": "user",
+        "tunnel_authentication": 0,
+        "tunnel_password": "123456"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Add server using SSH tunnel with identity file and save the password",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "ssh_tunnel": true,
+      "with_password": false,
+      "save_password": true,
+      "test_data": {
+        "use_ssh_tunnel": 1,
+        "tunnel_host": "127.0.0.1",
+        "tunnel_port": 22,
+        "tunnel_username": "user",
+        "tunnel_authentication": 1,
+        "tunnel_identity_file": "pkey_rsa",
+        "tunnel_password": "123456"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Add server with password and save password to true",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "with_pwd": true,
+      "with_save": true,
+      "owner_server": true,
+      "test_data": {
+        "service": "TestDB"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Add server with password and save password to false",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "with_pwd": true,
+      "with_save": false,
+      "owner_server": true,
+      "test_data": {
+        "service": "TestDB"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Add server without password and save password to true",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "with_pwd": false,
+      "with_save": true,
+      "owner_server": true,
+      "test_data": {
+        "service": "TestDB"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Add server with connect now",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "owner_server": true,
+      "test_data": {
+        "service": "TestDB"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    }
+  ],
+  "is_password_saved": [
+    {
+      "name": "Connect server with 'save password",
+      "url": "/browser/server/connect/",
+      "is_positive_test": true,
+      "test_data": {
+        "is_password_saved": true
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200,
+        "message": "Server connected."
+      }
+    }
+  ],
+  "get_server": [
+    {
+      "name": "Get a server URL",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Reload a server configuration",
+      "url": "/browser/server/reload/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get a server URL using wrong server id",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "incorrect_server_id": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 410
+      }
+    },
+    {
+      "name": "Get a server Node dependants",
+      "url": "/browser/server/dependent/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get a server Node dependency",
+      "url": "/browser/server/dependency/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get a server Node sql",
+      "url": "/browser/server/sql/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get a server Node msql",
+      "url": "/browser/server/msql/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get a server Node statistics",
+      "url": "/browser/server/stats/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get a server pgpass details",
+      "url": "/browser/server/check_pgpass/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 410
+      }
+    }
+  ],
+  "get_shared_server": [
+    {
+      "name": "Get a shared server",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "shared": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get a all shared server",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "shared": true,
+      "no_server_id": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get the all available shared server",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "no_server_id": true,
+      "shared": true,
+      "server_list": true,
+      "mocking_required": false,
+      "mock_data": {
+      },
+      "expected_data": {
+        "status_code": 200
+      }
+    }
+  ],
+  "get_all_server": [
+    {
+      "name": "Get the all children of server",
+      "url": "/browser/server/children/",
+      "is_positive_test": true,
+      "children": true,
+      "mocking_required": false,
+      "mock_data": {
+      },
+      "expected_data": {
+        "status_code": 500
+      }
+    },
+    {
+      "name": "Get the all available servers",
+      "url": "/browser/server/nodes/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {
+      },
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get the all available server of server group",
+      "url": "/browser/server/nodes/",
+      "is_positive_test": true,
+      "server_list": true,
+      "mocking_required": false,
+      "mock_data": {
+      },
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get the all available server of server group",
+      "url": "/browser/server/nodes/",
+      "is_positive_test": true,
+      "server_list": true,
+      "servers": true,
+      "mocking_required": false,
+      "mock_data": {
+      },
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Get the all connected servers",
+      "url": "/browser/server/nodes/",
+      "is_positive_test": true,
+      "server_list": true,
+      "servers": true,
+      "connected": true,
+      "mocking_required": false,
+      "mock_data": {
+      },
+      "expected_data": {
+        "status_code": 200
+      }
+    }
+  ],
+  "connect_server": [
+    {
+      "name": "Get a server connection",
+      "url": "/browser/server/connect/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "connect to a server using password",
+      "url": "/browser/server/connect/",
+      "is_positive_test": true,
+      "connect": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Disconnect server test",
+      "url": "/browser/server/connect/",
+      "is_positive_test": true,
+      "disconnect": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Disconnect server when wrong server id passed",
+      "url": "/browser/server/connect/",
+      "is_positive_test": true,
+      "disconnect": true,
+      "wrong_server_id": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 400
+      }
+    },
+    {
+      "name": "Reload a server configuration",
+      "url": "/browser/server/reload/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Error while creating server restore point",
+      "url": "/browser/server/restore_point/",
+      "is_positive_test": true,
+      "restore_point": true,
+      "test_data": {
+        "Named restore point created": "PLACE_HOLDER"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 500
+      }
+    }
+  ],
+  "delete_server": [
+    {
+      "name": "Delete a server URL",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Disconnect server test",
+      "url": "/browser/server/connect/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Error while fetching a server to delete",
+      "url": "/browser/server/obj/",
+      "is_positive_test": false,
+      "mocking_required": true,
+      "mock_data": {
+        "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_dict",
+        "return_value": "(True, 'Mocked Internal Server Error')"
+      },
+      "expected_data": {
+        "status_code": 500
+      }
+    },
+    {
+      "name": "server not found while deleting a server",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "invalid_server_id": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 410
+      }
+    }
+  ],
+  "update_server": [
+    {
+      "name": "update a server name",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "sslcompression": 1
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "update a server details without data",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "test_data": {},
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Update server with wrong server id",
+      "url": "/browser/server/obj/",
+      "is_positive_test": false,
+      "clear_save_password": true,
+      "wrong_server_id": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 410
+      }
+    },
+    {
+      "name": "Update server with incorrect hostaddr",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "hostaddr": "PLACE_HOLDER",
+        "db_res": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 400
+      }
+    },
+    {
+      "name": "update a server , make server shared",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "owner_server": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "shared": true
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Clear saved password",
+      "url": "/browser/server/clear_saved_password/",
+      "is_positive_test": true,
+      "clear_save_password": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Clear saved password with wrong server id",
+      "url": "/browser/server/clear_saved_password/",
+      "is_positive_test": false,
+      "clear_save_password": true,
+      "wrong_server_id": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "wal replay",
+      "url": "/browser/server/wal_replay/",
+      "is_positive_test": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 410
+      }
+    },
+    {
+      "name": "Clear ssh tunnel password",
+      "url": "/browser/server/clear_sshtunnel_password/",
+      "is_positive_test": true,
+      "clear_save_password": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Clear ssh tunnel password with wrong server id",
+      "url": "/browser/server/clear_sshtunnel_password/",
+      "is_positive_test": false,
+      "clear_save_password": true,
+      "wrong_server_id": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "error while clearing a ssh password",
+      "url": "/browser/server/clear_sshtunnel_password/",
+      "is_positive_test": false,
+      "error_clearing_password": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    }
+  ],
+  "update_shared_server": [
+    {
+      "name": "update a server name",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "sslcompression": 1
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "update a server details without data",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "test_data": {},
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Update server with wrong server id",
+      "url": "/browser/server/obj/",
+      "is_positive_test": false,
+      "clear_save_password": true,
+      "wrong_server_id": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 410
+      }
+    },
+    {
+      "name": "Update server with incorrect hostaddr",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "hostaddr": "PLACE_HOLDER",
+        "db_res": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER"
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 400
+      }
+    },
+    {
+      "name": "update a server , make server shared",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "owner_server": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "shared": true
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Clear saved password when login user is not owner of server",
+      "url": "/browser/server/clear_saved_password/",
+      "is_positive_test": true,
+      "clear_save_password": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Clear saved password with wrong server id",
+      "url": "/browser/server/clear_saved_password/",
+      "is_positive_test": false,
+      "clear_save_password": true,
+      "wrong_server_id": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Clear ssh tunnel password",
+      "url": "/browser/server/clear_sshtunnel_password/",
+      "is_positive_test": true,
+      "clear_save_password": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "Clear ssh tunnel password with wrong server id",
+      "url": "/browser/server/clear_sshtunnel_password/",
+      "is_positive_test": false,
+      "clear_save_password": true,
+      "wrong_server_id": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    },
+    {
+      "name": "error while clearing a ssh password",
+      "url": "/browser/server/clear_sshtunnel_password/",
+      "is_positive_test": false,
+      "error_clearing_password": true,
+      "test_data": {
+        "comment": "PLACE_HOLDER",
+        "id": "PLACE_HOLDER",
+        "is_password_saved": false
+      },
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    }
+  ],
+  "delete_multiple_server": [
+    {
+      "name": "Delete multiple server",
+      "url": "/browser/server/obj/",
+      "is_positive_test": true,
+      "mocking_required": false,
+      "mock_data": {},
+      "expected_data": {
+        "status_code": 200
+      }
+    }
+  ]
+}
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_add_server.py b/web/pgadmin/browser/server_groups/servers/tests/test_add_server.py
new file mode 100644
index 000000000..1b23fcdb8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_add_server.py
@@ -0,0 +1,84 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as servers_utils
+
+
+class AddServerTest(BaseTestGenerator):
+    """ This class will add the servers under default server group. """
+
+    scenarios = utils.generate_scenarios('add_server',
+                                         servers_utils.test_cases)
+
+    def setUp(self):
+        pass
+
+    def create_server(self, url):
+        return self.tester.post(
+            url,
+            data=json.dumps(self.server),
+            content_type='html/json'
+        )
+
+    def runTest(self):
+        """ This function will add the server under default server group."""
+        url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
+
+        # Add service name in the config
+        if 'connect_timeout' in self.test_data:
+            self.server['connect_timeout'] = self.test_data['connect_timeout']
+        elif 'shared' in self.test_data:
+            self.server['shared'] = self.test_data['shared']
+        elif 'service' in self.test_data:
+            self.server['service'] = self.test_data['service']
+
+        if hasattr(self, 'ssh_tunnel'):
+            self.server['use_ssh_tunnel'] = self.test_data['use_ssh_tunnel']
+            self.server['tunnel_host'] = self.test_data['tunnel_host']
+            self.server['tunnel_port'] = self.test_data['tunnel_port']
+            self.server['tunnel_username'] = self.test_data['tunnel_username']
+
+            if self.with_password:
+                self.server['tunnel_authentication'] = self.test_data[
+                    'tunnel_authentication']
+            else:
+                self.server['tunnel_authentication'] = 1
+                self.server['tunnel_identity_file'] = 'pkey_rsa'
+
+            if self.save_password:
+                self.server['tunnel_password'] = self.test_data[
+                    'tunnel_password']
+        if 'connect_now' in self.test_data:
+            self.server['connect_now'] = self.test_data['connect_now']
+            self.server['password'] = self.server['db_password']
+
+        if self.is_positive_test:
+            if hasattr(self, 'with_save'):
+                self.server['save_password'] = self.with_save
+            if hasattr(self, 'with_pwd') and not self.with_pwd:
+                # Remove the password from server object
+                db_password = self.server['db_password']
+                del self.server['db_password']
+            response = self.create_server(url)
+        self.assertEquals(response.status_code,
+                          self.expected_data["status_code"])
+        response_data = json.loads(response.data.decode('utf-8'))
+        self.server_id = response_data['node']['_id']
+
+        if hasattr(self, 'with_pwd') and not self.with_pwd:
+            # Remove the password from server object
+            self.server['db_password'] = db_password
+
+    def tearDown(self):
+        """This function delete the server from SQLite """
+        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_connect_timeout.py b/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_connect_timeout.py
deleted file mode 100644
index 94d555617..000000000
--- a/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_connect_timeout.py
+++ /dev/null
@@ -1,47 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2020, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-import json
-
-from pgadmin.utils.route import BaseTestGenerator
-from regression.python_test_utils import test_utils as utils
-
-
-class ServersWithConnectTimeoutAddTestCase(BaseTestGenerator):
-    """ This class will add the servers under default server group. """
-
-    scenarios = [
-        # Fetch the default url for server object
-        (
-            'Default Server Node url', dict(
-                url='/browser/server/obj/'
-            )
-        )
-    ]
-
-    def setUp(self):
-        pass
-
-    def runTest(self):
-        """ This function will add the server under default server group."""
-        url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
-        # Add service name in the config
-        self.server['connect_timeout'] = 5
-        response = self.tester.post(
-            url,
-            data=json.dumps(self.server),
-            content_type='html/json'
-        )
-        self.assertEqual(response.status_code, 200)
-        response_data = json.loads(response.data.decode('utf-8'))
-        self.server_id = response_data['node']['_id']
-
-    def tearDown(self):
-        """This function delete the server from SQLite """
-        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_service_id.py b/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_service_id.py
deleted file mode 100644
index 9b4d4d377..000000000
--- a/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_service_id.py
+++ /dev/null
@@ -1,47 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2020, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-import json
-
-from pgadmin.utils.route import BaseTestGenerator
-from regression.python_test_utils import test_utils as utils
-
-
-class ServersWithServiceIDAddTestCase(BaseTestGenerator):
-    """ This class will add the servers under default server group. """
-
-    scenarios = [
-        # Fetch the default url for server object
-        (
-            'Default Server Node url', dict(
-                url='/browser/server/obj/'
-            )
-        )
-    ]
-
-    def setUp(self):
-        pass
-
-    def runTest(self):
-        """ This function will add the server under default server group."""
-        url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
-        # Add service name in the config
-        self.server['service'] = "TestDB"
-        response = self.tester.post(
-            url,
-            data=json.dumps(self.server),
-            content_type='html/json'
-        )
-        self.assertEqual(response.status_code, 200)
-        response_data = json.loads(response.data.decode('utf-8'))
-        self.server_id = response_data['node']['_id']
-
-    def tearDown(self):
-        """This function delete the server from SQLite """
-        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py b/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py
deleted file mode 100644
index 7c0b005d9..000000000
--- a/web/pgadmin/browser/server_groups/servers/tests/test_add_server_with_ssh_tunnel.py
+++ /dev/null
@@ -1,82 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2020, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-import json
-
-from pgadmin.utils.route import BaseTestGenerator
-from regression.python_test_utils import test_utils as utils
-
-
-class ServersWithSSHTunnelAddTestCase(BaseTestGenerator):
-    """ This class will add the servers under default server group. """
-
-    scenarios = [
-        (
-            'Add server using SSH tunnel with password', dict(
-                url='/browser/server/obj/',
-                with_password=True,
-                save_password=False,
-            )
-        ),
-        (
-            'Add server using SSH tunnel with identity file', dict(
-                url='/browser/server/obj/',
-                with_password=False,
-                save_password=False,
-            )
-        ),
-        (
-            'Add server using SSH tunnel with password and saved it', dict(
-                url='/browser/server/obj/',
-                with_password=True,
-                save_password=True,
-            )
-        ),
-        (
-            'Add server using SSH tunnel with identity file and save the '
-            'password', dict(
-                url='/browser/server/obj/',
-                with_password=False,
-                save_password=True,
-            )
-        ),
-    ]
-
-    def setUp(self):
-        pass
-
-    def runTest(self):
-        """ This function will add the server under default server group."""
-        url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
-        # Add service name in the config
-        self.server['use_ssh_tunnel'] = 1
-        self.server['tunnel_host'] = '127.0.0.1'
-        self.server['tunnel_port'] = 22
-        self.server['tunnel_username'] = 'user'
-        if self.with_password:
-            self.server['tunnel_authentication'] = 0
-        else:
-            self.server['tunnel_authentication'] = 1
-            self.server['tunnel_identity_file'] = 'pkey_rsa'
-
-        if self.save_password:
-            self.server['tunnel_password'] = '123456'
-
-        response = self.tester.post(
-            url,
-            data=json.dumps(self.server),
-            content_type='html/json'
-        )
-        self.assertEqual(response.status_code, 200)
-        response_data = json.loads(response.data.decode('utf-8'))
-        self.server_id = response_data['node']['_id']
-
-    def tearDown(self):
-        """This function delete the server from SQLite """
-        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_all_server_get.py b/web/pgadmin/browser/server_groups/servers/tests/test_all_server_get.py
new file mode 100644
index 000000000..cd35a7a05
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_all_server_get.py
@@ -0,0 +1,78 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+import random
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from . import utils as servers_utils
+
+
+class AllServersGetTestCase(BaseTestGenerator):
+    """
+    This class will fetch added servers under default server group
+    by response code.
+    """
+
+    scenarios = utils.generate_scenarios('get_all_server',
+                                         servers_utils.test_cases)
+
+    def setUp(self):
+        """This function add the server to test the GET API"""
+        self.server['password'] = 'edb'
+        self.server_id = utils.create_server(self.server)
+        server_dict = {"server_id": self.server_id}
+        utils.write_node_info("sid", server_dict)
+
+    def get_server(self):
+        return self.tester.get(self.url, follow_redirects=True)
+
+    def connect_to_server(self, url):
+        return self.tester.post(
+            url,
+            data=self.server,
+            content_type='html/json'
+        )
+
+    def runTest(self):
+        """ This function will fetch the added servers to object browser. """
+        server_id = parent_node_dict["server"][-1]["server_id"]
+        if not server_id:
+            raise Exception("Server not found to test GET API")
+        response = None
+        if self.is_positive_test:
+            if hasattr(self, 'invalid_server_group'):
+                self.url = self.url + '{0}/{1}?_={1}'.format(
+                    utils.SERVER_GROUP, random.randint(1, 9999999))
+            elif hasattr(self, 'children'):
+
+                self.url = self.url + '{0}/{1}'.format(
+                    utils.SERVER_GROUP, server_id)
+            elif hasattr(self, 'server_list'):
+                if hasattr(self, 'servers'):
+                    server_id = ''
+                self.url = self.url + '{0}/{1}'.format(
+                    utils.SERVER_GROUP, server_id)
+            else:
+                if hasattr(self, "connected"):
+                    url = '/browser/server/connect/' + '{0}/{1}'.format(
+                        utils.SERVER_GROUP,
+                        self.server_id)
+                    self.server['password'] = 'edb'
+
+                    self.connect_to_server(url)
+                self.url = self.url + '{0}/{1}?_={2}'.format(
+                    utils.SERVER_GROUP, server_id, random.randint(1, 9999999))
+            response = self.get_server()
+        self.assertEquals(response.status_code,
+                          self.expected_data["status_code"])
+
+    def tearDown(self):
+        """This function delete the server from SQLite """
+        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_check_connect.py b/web/pgadmin/browser/server_groups/servers/tests/test_check_connect.py
new file mode 100644
index 000000000..6d49cea7b
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_check_connect.py
@@ -0,0 +1,107 @@
+##########################################################################
+#
+# 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 regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from . import utils as servers_utils
+import json
+
+
+class ServersConnectTestCase(BaseTestGenerator):
+    """
+    This class will fetch added servers under default server group
+    by response code.
+    """
+
+    scenarios = utils.generate_scenarios('connect_server',
+                                         servers_utils.test_cases)
+
+    def get_ssh_tunnel(self):
+        print("in_get_ssh")
+        self.server['use_ssh_tunnel'] = 1
+        self.server['tunnel_host'] = '127.0.0.1'
+        self.server['tunnel_port'] = 22
+        self.server['tunnel_username'] = 'user'
+        if self.with_password:
+            self.server['tunnel_authentication'] = 0
+        else:
+            self.server['tunnel_authentication'] = 1
+            self.server['tunnel_identity_file'] = 'pkey_rsa'
+
+        if self.save_password:
+            self.server['tunnel_password'] = '123456'
+
+    def setUp(self):
+        """This function add the server to test the GET API"""
+
+        self.server_id = utils.create_server(self.server)
+        server_dict = {"server_id": self.server_id}
+        utils.write_node_info("sid", server_dict)
+
+    def get_server_connection(self, server_id):
+        return self.tester.get(self.url + str(utils.SERVER_GROUP) + '/' +
+                               str(server_id),
+                               follow_redirects=True)
+
+    def server_disonnect(self, server_id):
+        return self.tester.delete(self.url + str(utils.SERVER_GROUP) + '/' +
+                                  str(server_id))
+
+    def connect_to_server(self, url):
+        return self.tester.post(
+            url,
+            data=json.dumps(self.server),
+            content_type='html/json'
+        )
+
+    def add_server_details(self, url):
+        return self.tester.post(
+            url,
+            data=str(self.test_data),
+            content_type='html/json'
+        )
+
+    def runTest(self):
+        """ This function will fetch the added servers to object browser. """
+        server_id = parent_node_dict["server"][-1]["server_id"]
+        if not server_id:
+            raise Exception("Server not found to test GET API")
+        response = None
+        if self.is_positive_test:
+            if hasattr(self, 'disconnect'):
+                if hasattr(self, 'wrong_server_id'):
+                    server_id = 99999
+                response = self.server_disonnect(server_id)
+            elif hasattr(self, "connect"):
+                url = self.url + '{0}/{1}'.format(
+                    utils.SERVER_GROUP,
+                    self.server_id)
+                self.server['password'] = self.server['db_password']
+                response = self.connect_to_server(url)
+            elif hasattr(self, 'restore_point') or hasattr(self,
+                                                           'change_password'):
+                connect_url = '/browser/server/connect/{0}/{1}'.format(
+                    utils.SERVER_GROUP,
+                    self.server_id)
+                url = self.url + '{0}/{1}'.format(
+                    utils.SERVER_GROUP,
+                    self.server_id)
+
+                self.connect_to_server(connect_url)
+                response = self.add_server_details(url)
+            else:
+                response = self.get_server_connection(server_id)
+
+        self.assertEquals(response.status_code,
+                          self.expected_data["status_code"])
+
+    def tearDown(self):
+        """This function delete the server from SQLite """
+        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_is_password_saved.py b/web/pgadmin/browser/server_groups/servers/tests/test_is_password_saved.py
new file mode 100644
index 000000000..7d144d3d7
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_is_password_saved.py
@@ -0,0 +1,52 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2020, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+import json
+
+from pgadmin.utils.route import BaseTestGenerator
+from regression.python_test_utils import test_utils as utils
+from . import utils as servers_utils
+
+
+class IsPasswordSaved(BaseTestGenerator):
+    """ This class will test the save password functionality. """
+
+    scenarios = utils.generate_scenarios('is_password_saved',
+                                         servers_utils.test_cases)
+
+    def setUp(self):
+        self.server_id = utils.create_server(self.server)
+        server_dict = {"server_id": self.server_id}
+        utils.write_node_info("sid", server_dict)
+
+    def runTest(self):
+        """This function will execute the connect server APIs"""
+        response = self.tester.post(
+            self.url + str(utils.SERVER_GROUP) + '/' + str(self.server_id),
+            data=dict(
+                password=self.server['db_password'],
+                save_password='on'),
+            follow_redirects=True)
+
+        expected_status_code = self.expected_data["status_code"]
+        actual_status_code = response.status_code
+        self.assertEquals(actual_status_code, expected_status_code)
+        response_data = json.loads(response.data.decode('utf-8'))
+
+        expected_message = self.expected_data["message"]
+        actual_message = response_data["info"]
+        self.assertEquals(actual_message, expected_message)
+
+        expected_is_password_saved = self.test_data["is_password_saved"]
+        actual_is_password_saved = response_data["data"]["is_password_saved"]
+        self.assertEquals(actual_is_password_saved, expected_is_password_saved)
+
+    def tearDown(self):
+        """This function delete the server from SQLite """
+        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_server_add.py b/web/pgadmin/browser/server_groups/servers/tests/test_server_add.py
deleted file mode 100644
index 8444e1d72..000000000
--- a/web/pgadmin/browser/server_groups/servers/tests/test_server_add.py
+++ /dev/null
@@ -1,86 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2020, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-import json
-import copy
-from pgadmin.utils.route import BaseTestGenerator
-from regression.python_test_utils import test_utils as utils
-
-
-class ServersAddTestCase(BaseTestGenerator):
-    """ This class will add the servers under default server group. """
-
-    scenarios = [
-        # Fetch the default url for server object
-        ('Default Server Node url', dict(url='/browser/server/obj/'))
-    ]
-
-    def setUp(self):
-        pass
-
-    def runTest(self):
-        """ This function will add the server under default server group."""
-        url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
-        response = self.tester.post(url, data=json.dumps(self.server),
-                                    content_type='html/json')
-        self.assertEqual(response.status_code, 200)
-        response_data = json.loads(response.data.decode('utf-8'))
-        self.server_id = response_data['node']['_id']
-        server_dict = {"server_id": int(self.server_id)}
-        utils.write_node_info("sid", server_dict)
-
-    def tearDown(self):
-        """This function delete the server from SQLite """
-        utils.delete_server_with_api(self.tester, self.server_id)
-
-
-class AddServersWithSavePasswordTestCase(BaseTestGenerator):
-    """ This class will add the servers under default server group. """
-
-    scenarios = [
-        # Fetch the default url for server object
-        ('Add server with password and save password to true',
-         dict(url='/browser/server/obj/', with_pwd=True, with_save=True)),
-        ('Add server with password and save password to false',
-         dict(url='/browser/server/obj/', with_pwd=True, with_save=False)),
-        ('Add server without password and save password to true',
-         dict(url='/browser/server/obj/', with_pwd=False, with_save=True)),
-    ]
-
-    def setUp(self):
-        pass
-
-    def runTest(self):
-        """ This function will add the server under default server group."""
-        url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
-        _server = copy.deepcopy(self.server)
-        # Update the flag as required
-        _server['save_password'] = self.with_save
-        if not self.with_pwd:
-            # Remove the password from server object
-            del _server['db_password']
-
-        response = self.tester.post(url, data=json.dumps(_server),
-                                    content_type='html/json')
-        self.assertEqual(response.status_code, 200)
-        response_data = json.loads(response.data.decode('utf-8'))
-        self.server_id = response_data['node']['_id']
-        server_dict = {"server_id": int(self.server_id)}
-        # Fetch the node info to check if password was saved or not
-        response = self.tester.get(self.url.replace('obj', 'nodes') +
-                                   str(utils.SERVER_GROUP) + '/' +
-                                   str(self.server_id),
-                                   follow_redirects=True)
-        self.assertEqual(response.status_code, 200)
-        self.assertTrue('is_password_saved' in response.json['result'])
-        utils.write_node_info("sid", server_dict)
-
-    def tearDown(self):
-        """This function delete the server from SQLite """
-        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_server_delete.py b/web/pgadmin/browser/server_groups/servers/tests/test_server_delete.py
index 30917bd3c..78911f5a8 100644
--- a/web/pgadmin/browser/server_groups/servers/tests/test_server_delete.py
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_server_delete.py
@@ -9,15 +9,14 @@
 
 from pgadmin.utils.route import BaseTestGenerator
 from regression.python_test_utils import test_utils as utils
+from . import utils as servers_utils
 
 
 class ServerDeleteTestCase(BaseTestGenerator):
     """ This class will delete the last server present under tree node."""
 
-    scenarios = [
-        # Fetching the default url for server node
-        ('Default Server Node url', dict(url='/browser/server/obj/'))
-    ]
+    scenarios = utils.generate_scenarios('delete_server',
+                                         servers_utils.test_cases)
 
     def setUp(self):
         """This function add the server to test the DELETE API"""
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_server_get.py b/web/pgadmin/browser/server_groups/servers/tests/test_server_get.py
index 0b4dc183b..5a83e2a08 100644
--- a/web/pgadmin/browser/server_groups/servers/tests/test_server_get.py
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_server_get.py
@@ -10,6 +10,8 @@
 from pgadmin.utils.route import BaseTestGenerator
 from regression import parent_node_dict
 from regression.python_test_utils import test_utils as utils
+from . import utils as servers_utils
+import json
 
 
 class ServersGetTestCase(BaseTestGenerator):
@@ -18,26 +20,50 @@ class ServersGetTestCase(BaseTestGenerator):
     by response code.
     """
 
-    scenarios = [
-        # Fetch the default url for server node
-        ('Default Server Node url', dict(url='/browser/server/obj/'))
-    ]
+    scenarios = utils.generate_scenarios('get_server',
+                                         servers_utils.test_cases)
 
     def setUp(self):
         """This function add the server to test the GET API"""
-        self.server_id = utils.create_server(self.server)
+        if hasattr(self, 'shared'):
+
+            self.server['shared'] = True
+            url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
+            response = self.tester.post(
+                url,
+                data=json.dumps(self.server),
+                content_type='html/json'
+            )
+            response_data = json.loads(response.data.decode('utf-8'))
+            self.server_id = response_data['node']['_id']
+        else:
+            self.server_id = utils.create_server(self.server)
         server_dict = {"server_id": self.server_id}
         utils.write_node_info("sid", server_dict)
 
+    def get_server(self, server_id):
+        return self.tester.get(self.url + str(utils.SERVER_GROUP) + '/' +
+                               str(server_id),
+                               follow_redirects=True)
+
     def runTest(self):
         """ This function will fetch the added servers to object browser. """
         server_id = parent_node_dict["server"][-1]["server_id"]
         if not server_id:
             raise Exception("Server not found to test GET API")
-        response = self.tester.get(self.url + str(utils.SERVER_GROUP) + '/' +
-                                   str(server_id),
-                                   follow_redirects=True)
-        self.assertEqual(response.status_code, 200)
+        response = None
+        if self.is_positive_test:
+            if hasattr(self, "incorrect_server_id"):
+                server_id = 9999
+            if hasattr(self, "server_list"):
+                server_id = ''
+            if hasattr(self, "server_node"):
+                server_id = ''
+            if hasattr(self, 'shared'):
+                server_id = self.server_id
+            response = self.get_server(server_id)
+        self.assertEquals(response.status_code,
+                          self.expected_data["status_code"])
 
     def tearDown(self):
         """This function delete the server from SQLite """
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_server_put.py b/web/pgadmin/browser/server_groups/servers/tests/test_server_put.py
index 31c7a476e..74674c7cb 100644
--- a/web/pgadmin/browser/server_groups/servers/tests/test_server_put.py
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_server_put.py
@@ -11,32 +11,61 @@ import json
 
 from pgadmin.utils.route import BaseTestGenerator
 from regression.python_test_utils import test_utils as utils
+from . import utils as servers_utils
 
 
 class ServerUpdateTestCase(BaseTestGenerator):
     """ This class will update server's comment field. """
 
-    scenarios = [
-        # Fetching the default url for server node
-        ('Default Server Node url', dict(url='/browser/server/obj/'))
-    ]
+    scenarios = utils.generate_scenarios('update_server',
+                                         servers_utils.test_cases)
 
     def setUp(self):
         """This function add the server to test the PUT API"""
-        self.server_id = utils.create_server(self.server)
+        if hasattr(self, 'clear_save_password'):
+            self.server['save_password'] = 1
+        create_server_url = "/browser/server/obj/{0}/".format(
+            utils.SERVER_GROUP)
+
+        self.server_id = \
+            servers_utils.create_server_with_api(self, create_server_url)
         server_dict = {"server_id": self.server_id}
         utils.write_node_info("sid", server_dict)
 
+    def update_server(self):
+        return self.tester.put(
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id), data=json.dumps(self.test_data),
+            content_type='html/json')
+
+    def connect_to_server(self, url):
+        return self.tester.post(
+            url,
+            data=json.dumps(self.server),
+            content_type='html/json'
+        )
+
     def runTest(self):
         """This function update the server details"""
         if not self.server_id:
             raise Exception("No server to update.")
-        data = {"comment": self.server['comment'], "id": self.server_id}
-        put_response = self.tester.put(
-            self.url + str(utils.SERVER_GROUP) + '/' +
-            str(self.server_id), data=json.dumps(data),
-            content_type='html/json')
-        self.assertEqual(put_response.status_code, 200)
+        if 'comment' in self.test_data:
+            self.test_data["comment"] = self.server['comment']
+        self.test_data["id"] = self.server_id
+        if self.is_positive_test:
+            if hasattr(self, 'server_connected'):
+                url = '/browser/server/connect/{0}/{1}'.format(
+                    utils.SERVER_GROUP,
+                    self.server_id)
+                self.server['password'] = self.server['db_password']
+                self.connect_to_server(url)
+            put_response = self.update_server()
+        else:
+            if hasattr(self, 'wrong_server_id'):
+                self.server_id = 9999
+            put_response = self.update_server()
+        self.assertEquals(put_response.status_code,
+                          self.expected_data["status_code"])
 
     def tearDown(self):
         """This function delete the server from SQLite"""
diff --git a/web/pgadmin/browser/server_groups/servers/tests/test_shared_server.py b/web/pgadmin/browser/server_groups/servers/tests/test_shared_server.py
new file mode 100644
index 000000000..b3f8215a4
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tests/test_shared_server.py
@@ -0,0 +1,134 @@
+##########################################################################
+#
+# 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 regression import parent_node_dict
+from regression.python_test_utils import test_utils as utils
+from . import utils as servers_utils
+import json
+from regression.test_setup import config_data
+from regression.python_test_utils.test_utils import \
+    create_user_wise_test_client
+import config
+
+test_user_details = config_data[
+    'pgAdmin4_test_non_admin_credentials']
+
+
+class SharedServersGetTestCase(BaseTestGenerator):
+    """
+    This class will fetch added servers under default server group
+    by response code.
+    """
+
+    scenarios = utils.generate_scenarios('get_shared_server',
+                                         servers_utils.test_cases)
+
+    def setUp(self):
+        """This function add the server to test the GET API"""
+
+        if config.SERVER_MODE is False:
+            self.skipTest(
+                "Can not run shared servers test cases in the SERVER mode."
+            )
+        self.server['shared'] = True
+        url = "{0}{1}/".format(self.url, utils.SERVER_GROUP)
+        response = self.tester.post(
+            url,
+            data=json.dumps(self.server),
+            content_type='html/json'
+        )
+        response_data = json.loads(response.data.decode('utf-8'))
+        self.server_id = response_data['node']['_id']
+
+        server_dict = {"server_id": self.server_id}
+        utils.write_node_info("sid", server_dict)
+
+    def get_server(self, server_id):
+        return self.tester.get(self.url + str(utils.SERVER_GROUP) + '/' +
+                               str(server_id),
+                               follow_redirects=True)
+
+    @create_user_wise_test_client(test_user_details)
+    def runTest(self):
+        """ This function will fetch the added servers to object browser. """
+        if not self.server_id:
+            raise Exception("Server not found to test GET API")
+        response = None
+        if self.is_positive_test:
+            if hasattr(self, 'no_server_id'):
+                if hasattr(self, 'server_list'):
+                    self.url = '/browser/server/nodes/'
+                server_id = ''
+                response = self.get_server(server_id)
+            else:
+                response = self.get_server(self.server_id)
+        self.assertEquals(response.status_code,
+                          self.expected_data["status_code"])
+
+    def tearDown(self):
+        """This function delete the server from SQLite """
+        utils.delete_server_with_api(self.tester, self.server_id)
+
+
+class SharedServerUpdateTestCase(BaseTestGenerator):
+    """ This class will update server's comment field. """
+
+    scenarios = utils.generate_scenarios('update_shared_server',
+                                         servers_utils.test_cases)
+
+    def setUp(self):
+        """This function add the server to test the PUT API"""
+        if config.SERVER_MODE is False:
+            self.skipTest(
+                "Can not run shared servers test cases in the Desktop mode."
+            )
+        self.server['shared'] = True
+        if hasattr(self, 'clear_save_password'):
+            self.server['save_password'] = 1
+        create_server_url = "/browser/server/obj/{0}/".format(
+            utils.SERVER_GROUP)
+
+        self.server_id = \
+            servers_utils.create_server_with_api(self, create_server_url)
+        server_dict = {"server_id": self.server_id}
+        utils.write_node_info("sid", server_dict)
+
+    def update_server(self):
+        return self.tester.put(
+            self.url + str(utils.SERVER_GROUP) + '/' +
+            str(self.server_id), data=json.dumps(self.test_data),
+            content_type='html/json')
+
+    def connect_to_server(self, url):
+        return self.tester.post(
+            url,
+            data=json.dumps(self.server),
+            content_type='html/json'
+        )
+
+    def runTest(self):
+        """This function update the server details"""
+        if not self.server_id:
+            raise Exception("No server to update.")
+        if 'comment' in self.test_data:
+            self.test_data["comment"] = self.server['comment']
+        self.test_data["id"] = self.server_id
+        if self.is_positive_test:
+            put_response = self.update_server()
+        else:
+            if hasattr(self, 'wrong_server_id'):
+                self.server_id = 9999
+            put_response = self.update_server()
+        self.assertEquals(put_response.status_code,
+                          self.expected_data["status_code"])
+
+    def tearDown(self):
+        """This function delete the server from SQLite"""
+        utils.delete_server_with_api(self.tester, self.server_id)
diff --git a/web/pgadmin/browser/server_groups/servers/tests/utils.py b/web/pgadmin/browser/server_groups/servers/tests/utils.py
new file mode 100644
index 000000000..8680b29fb
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/servers/tests/utils.py
@@ -0,0 +1,52 @@
+import os
+import json
+import sqlite3
+import config
+from regression.python_test_utils import test_utils as utils
+
+
+CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
+with open(CURRENT_PATH + "/servers_test_data.json") as data_file:
+    test_cases = json.load(data_file)
+
+
+def create_server(server, SERVER_GROUP):
+    """This function is used to create server"""
+    try:
+        conn = sqlite3.connect(config.TEST_SQLITE_PATH)
+        # Create the server
+        cur = conn.cursor()
+        if 'shared' not in server:
+            server['shared'] = False
+        server_details = (1, SERVER_GROUP, server['name'], server['host'],
+                          server['port'], server['db'], server['username'],
+                          server['role'], server['sslmode'], server['comment'],
+                          server['shared'])
+        cur.execute('INSERT INTO server (user_id, servergroup_id, name, host, '
+                    'port, maintenance_db, username, role, ssl_mode,'
+                    ' comment, shared) VALUES (?,?,?,?,?,?,?,?,?,?,?)',
+                    server_details)
+        server_id = cur.lastrowid
+        conn.commit()
+        conn.close()
+
+        type = utils.get_server_type(server)
+        server['type'] = type
+
+        return server_id
+    except Exception as exception:
+        raise Exception("Error while creating server. %s" % exception)
+
+
+def create_server_with_api(self, url):
+    try:
+        response = self.tester.post(
+            url,
+            data=json.dumps(self.server),
+            content_type='html/json'
+        )
+        response_data = json.loads(response.data.decode('utf-8'))
+        server_id = response_data['node']['_id']
+        return server_id
+    except Exception as exception:
+        raise Exception("Error while creating server. %s" % exception)
diff --git a/web/pgadmin/browser/server_groups/static/img/server_group_shared.svg b/web/pgadmin/browser/server_groups/static/img/server_group_shared.svg
new file mode 100644
index 000000000..6a4e22bf8
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/static/img/server_group_shared.svg
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#F2F2F2;}
+	.st1{fill:#9BA5B0;}
+	.st2{fill:none;stroke:#9BA5B0;stroke-width:0.75;stroke-miterlimit:1;}
+	.st3{fill:#F7F7F7;}
+	.st4{fill:#354A5F;}
+</style>
+<g>
+	<path class="st0" d="M6.7,2.1h4.6c0.6,0,1.1,0.5,1.1,1.1V4c0,0.6-0.5,1.1-1.1,1.1H6.7C6.1,5.1,5.6,4.6,5.6,4V3.2
+		C5.6,2.6,6.1,2.1,6.7,2.1z"/>
+	<path class="st1" d="M11.3,2.5c0.4,0,0.8,0.3,0.8,0.8v0.8c0,0.4-0.3,0.8-0.8,0.8H6.7C6.3,4.8,6,4.5,6,4.1V3.2
+		c0-0.4,0.3-0.7,0.8-0.7H11.3 M11.3,1.7H6.7c-0.8,0-1.5,0.7-1.5,1.5V4c0,0.8,0.7,1.5,1.5,1.5h4.6c0.8,0,1.5-0.7,1.5-1.5V3.2
+		C12.8,2.4,12.1,1.7,11.3,1.7L11.3,1.7z"/>
+	<line class="st2" x1="8.9" y1="3.6" x2="6.8" y2="3.6"/>
+	<path class="st0" d="M6.7,5.2h4.6c0.6,0,1.1,0.5,1.1,1.1v0.8c0,0.6-0.5,1.1-1.1,1.1H6.7c-0.6,0-1.1-0.5-1.1-1.1V6.3
+		C5.6,5.7,6.1,5.2,6.7,5.2z"/>
+	<path class="st1" d="M11.3,5.6c0.4,0,0.8,0.3,0.8,0.8v0.8c0,0.4-0.3,0.8-0.8,0.8H6.7C6.3,7.9,6,7.6,6,7.1V6.3
+		c0-0.4,0.3-0.7,0.8-0.7L11.3,5.6 M11.3,4.8H6.7c-0.8,0-1.5,0.7-1.5,1.5v0.8c0,0.8,0.7,1.5,1.5,1.5h4.6c0.8,0,1.5-0.7,1.5-1.5V6.3
+		C12.8,5.5,12.1,4.8,11.3,4.8L11.3,4.8z"/>
+	<line class="st2" x1="8.9" y1="6.7" x2="6.8" y2="6.7"/>
+	<path class="st0" d="M6.7,8.3h4.6c0.6,0,1.1,0.5,1.1,1.1v0.9c0,0.6-0.5,1.1-1.1,1.1l0,0H6.7c-0.6,0-1.1-0.5-1.1-1.1l0,0V9.4
+		C5.6,8.8,6.1,8.3,6.7,8.3L6.7,8.3z"/>
+	<path class="st1" d="M11.3,8.7c0.4,0,0.8,0.3,0.8,0.8v0.8c0,0.4-0.3,0.8-0.8,0.8H6.7C6.3,11,6,10.7,6,10.3V9.4C6,9,6.3,8.7,6.7,8.7
+		H11.3 M11.3,7.9H6.7c-0.8,0-1.5,0.7-1.5,1.5v0.8c0,0.8,0.7,1.5,1.5,1.5h4.6c0.8,0,1.5-0.7,1.5-1.5V9.4C12.8,8.6,12.1,7.9,11.3,7.9z
+		"/>
+	<line class="st2" x1="8.9" y1="9.8" x2="6.8" y2="9.8"/>
+</g>
+<path class="st3" d="M11.6,10.5c-0.6,0.3-0.8,0.1-1.1-0.2c-0.1-0.2-0.3-0.4-0.6-0.5L7.1,8.5c-0.3-0.1-0.7-0.2-1-0.2
+	C5.1,8.1,4.2,8.4,3.4,9L2,10.1v2.7h0.9v-0.2l3.7,1.8c0.4,0.2,0.7,0.3,1.1,0.3c0.3,0,0.7-0.1,1-0.2l6.1-2.6c0.3-0.1,0.6-0.4,0.7-0.7
+	c0.1-0.3,0.1-0.7,0-1c-0.3-0.6-1.1-0.9-1.7-0.6l-0.4,0.2L11.6,10.5"/>
+<path class="st4" d="M15.5,10.2c-0.3-0.6-1.1-0.9-1.7-0.6l-0.4,0.2l-1.8,0.8l0.3,0.8l1.8-0.8l0.4-0.2c0.2-0.1,0.5,0,0.6,0.2
+	c0.1,0.1,0.1,0.2,0,0.3c0,0.1-0.1,0.2-0.2,0.2l-6.1,2.6c-0.5,0.2-1,0.2-1.4,0l-4.1-2v-1.2l1-0.8C4.5,9.2,5.2,9,6,9.1
+	c0.3,0,0.6,0.1,0.8,0.2l2.8,1.2c0.1,0,0.1,0.1,0.2,0.2c0.1,0.1,0.1,0.3,0,0.4c0,0,0,0.1-0.1,0.1c-0.1,0.1-0.3,0.2-0.5,0.1l-2.2-1
+	l-0.4,0.8l2.2,1c0.2,0.1,0.4,0.1,0.5,0.1c0.4,0,0.7-0.2,1-0.4c0.1-0.1,0.2-0.2,0.2-0.3c0.2-0.4,0.1-0.9-0.1-1.2
+	c-0.1-0.2-0.3-0.4-0.6-0.5L7.1,8.5c-0.3-0.1-0.7-0.2-1-0.2C5.1,8.1,4.2,8.4,3.4,9L2.9,9.4V9.3c0-0.6-0.4-1-1-1H1.4c-0.6,0-1,0.4-1,1
+	v4.1c0,0.6,0.4,1,1,1h0.5c0.6,0,1-0.4,1-1v-0.6l0,0v-0.2l3.7,1.8c0.4,0.2,0.7,0.3,1.1,0.3c0.3,0,0.7-0.1,1-0.2l6.1-2.6
+	c0.3-0.1,0.6-0.4,0.7-0.7C15.7,10.9,15.7,10.5,15.5,10.2z M1.9,13.4H1.4V9.3h0.5V13.4z"/>
+</svg>
diff --git a/web/pgadmin/browser/server_groups/static/js/server_group.js b/web/pgadmin/browser/server_groups/static/js/server_group.js
index 9bd7df0f3..ac1a0c821 100644
--- a/web/pgadmin/browser/server_groups/static/js/server_group.js
+++ b/web/pgadmin/browser/server_groups/static/js/server_group.js
@@ -9,8 +9,8 @@
 
 define('pgadmin.node.server_group', [
   'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
-  'sources/pgadmin', 'pgadmin.browser', 'pgadmin.browser.node',
-], function(gettext, url_for, $, _, pgAdmin) {
+  'sources/pgadmin', 'pgadmin.user_management.current_user', 'pgadmin.browser', 'pgadmin.browser.node',
+], function(gettext, url_for, $, _, pgAdmin, current_user) {
 
   if (!pgAdmin.Browser.Nodes['server_group']) {
     pgAdmin.Browser.Nodes['server_group'] = pgAdmin.Browser.Node.extend({
@@ -39,14 +39,25 @@ define('pgadmin.node.server_group', [
         defaults: {
           id: undefined,
           name: null,
+          user_id: undefined,
         },
         schema: [
           {
             id: 'id', label: gettext('ID'), type: 'int', group: null,
             mode: ['properties'],
+            visible: function(model){
+              if (model.attributes.user_id != current_user.id && !current_user.is_admin)
+                return false;
+              return true;
+            },
           },{
             id: 'name', label: gettext('Name'), type: 'text', group: null,
             mode: ['properties', 'edit', 'create'],
+            disabled: function(model){
+              if (model.attributes.user_id != current_user.id && !_.isUndefined(model.attributes.user_id))
+                return true;
+              return false;
+            },
           },
         ],
         validate: function() {
@@ -69,7 +80,12 @@ define('pgadmin.node.server_group', [
           return null;
         },
       }),
-      canDrop: function(itemData) { return itemData.can_delete; },
+
+      canDrop: function(itemData) {
+        var serverOwner = itemData.user_id;
+        if (serverOwner != current_user.id)
+          return false;
+        return true; },
       dropAsRemove: true,
       canDelete: function(i) {
         var s = pgAdmin.Browser.tree.siblings(i, true);
diff --git a/web/pgadmin/browser/server_groups/templates/css/server_group.css b/web/pgadmin/browser/server_groups/templates/css/server_group.css
new file mode 100644
index 000000000..3b5b8e58a
--- /dev/null
+++ b/web/pgadmin/browser/server_groups/templates/css/server_group.css
@@ -0,0 +1,17 @@
+.icon-server_group {
+  background-image: url('{{ url_for('NODE-server_group.static', filename='img/server_group.svg') }}') !important;
+  background-repeat: no-repeat;
+  background-size: 20px !important;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
+
+.icon-server_group_shared {
+  background-image: url('{{ url_for('NODE-server_group.static', filename='img/server_group_shared.svg') }}') !important;
+  background-repeat: no-repeat;
+  background-size: 20px !important;
+  align-content: center;
+  vertical-align: middle;
+  height: 1.3em;
+}
diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js
index f5ab09399..0eb7840b0 100644
--- a/web/pgadmin/browser/static/js/browser.js
+++ b/web/pgadmin/browser/static/js/browser.js
@@ -19,7 +19,7 @@ define('pgadmin.browser', [
   'pgadmin.browser.error', 'pgadmin.browser.frame',
   'pgadmin.browser.node', 'pgadmin.browser.collection', 'pgadmin.browser.activity',
   'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
-  'pgadmin.browser.keyboard', 'sources/tree/pgadmin_tree_save_state',
+  'pgadmin.browser.keyboard', 'sources/tree/pgadmin_tree_save_state','jquery.acisortable', 'jquery.acifragment',
 ], function(
   tree,
   gettext, url_for, require, $, _,
diff --git a/web/pgadmin/browser/templates/browser/js/utils.js b/web/pgadmin/browser/templates/browser/js/utils.js
index 33dd6809e..516b62697 100644
--- a/web/pgadmin/browser/templates/browser/js/utils.js
+++ b/web/pgadmin/browser/templates/browser/js/utils.js
@@ -44,6 +44,7 @@ define('pgadmin.browser.utils',
 
   pgAdmin['csrf_token_header'] = '{{ current_app.config.get('WTF_CSRF_HEADERS')[0] }}';
   pgAdmin['csrf_token'] = '{{ csrf_token() }}';
+  pgAdmin['server_mode'] = '{{ current_app.config.get('SERVER_MODE') }}';
 
   /* Get the inactivity related config */
   pgAdmin['user_inactivity_timeout'] = {{ current_app.config.get('USER_INACTIVITY_TIMEOUT') }};
diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py
index 03681e120..b33adc062 100644
--- a/web/pgadmin/model/__init__.py
+++ b/web/pgadmin/model/__init__.py
@@ -29,7 +29,7 @@ from flask_sqlalchemy import SQLAlchemy
 #
 ##########################################################################
 
-SCHEMA_VERSION = 25
+SCHEMA_VERSION = 26
 
 ##########################################################################
 #
@@ -173,6 +173,7 @@ class Server(db.Model):
     )
     tunnel_identity_file = db.Column(db.String(64), nullable=True)
     tunnel_password = db.Column(db.String(64), nullable=True)
+    shared = db.Column(db.Boolean(), nullable=False)
 
 
 class ModulePreference(db.Model):
@@ -305,3 +306,88 @@ class Database(db.Model):
         nullable=False,
         primary_key=True
     )
+
+
+class SharedServer(db.Model):
+    """Define a shared Postgres server"""
+
+    __tablename__ = 'sharedserver'
+    id = db.Column(db.Integer, primary_key=True)
+    user_id = db.Column(
+        db.Integer,
+        db.ForeignKey('user.id')
+    )
+    server_owner = db.Column(
+        db.String(128),
+        db.ForeignKey('user.username')
+    )
+    servergroup_id = db.Column(
+        db.Integer,
+        db.ForeignKey('servergroup.id'),
+        nullable=False
+    )
+    name = db.Column(db.String(128), nullable=False)
+    host = db.Column(db.String(128), nullable=True)
+    hostaddr = db.Column(db.String(128), nullable=True)
+    port = db.Column(
+        db.Integer(),
+        db.CheckConstraint('port >= 1 AND port <= 65534'),
+        nullable=False)
+    maintenance_db = db.Column(db.String(64), nullable=True)
+    username = db.Column(db.String(64), nullable=False)
+    password = db.Column(db.String(64), nullable=True)
+    save_password = db.Column(
+        db.Integer(),
+        db.CheckConstraint('save_password >= 0 AND save_password <= 1'),
+        nullable=False
+    )
+    role = db.Column(db.String(64), nullable=True)
+    ssl_mode = db.Column(
+        db.String(16),
+        db.CheckConstraint(
+            "ssl_mode IN ('allow', 'prefer', 'require', 'disable', "
+            "'verify-ca', 'verify-full')"
+        ),
+        nullable=False)
+    comment = db.Column(db.String(1024), nullable=True)
+    discovery_id = db.Column(db.String(128), nullable=True)
+    servers = db.relationship(
+        'ServerGroup',
+        backref=db.backref('sharedserver', cascade="all, delete-orphan"),
+        lazy='joined'
+    )
+    db_res = db.Column(db.Text(), nullable=True)
+    passfile = db.Column(db.Text(), nullable=True)
+    sslcert = db.Column(db.Text(), nullable=True)
+    sslkey = db.Column(db.Text(), nullable=True)
+    sslrootcert = db.Column(db.Text(), nullable=True)
+    sslcrl = db.Column(db.Text(), nullable=True)
+    sslcompression = db.Column(
+        db.Integer(),
+        db.CheckConstraint('sslcompression >= 0 AND sslcompression <= 1'),
+        nullable=False
+    )
+    bgcolor = db.Column(db.Text(10), nullable=True)
+    fgcolor = db.Column(db.Text(10), nullable=True)
+    service = db.Column(db.Text(), nullable=True)
+    connect_timeout = db.Column(db.Integer(), nullable=False)
+    use_ssh_tunnel = db.Column(
+        db.Integer(),
+        db.CheckConstraint('use_ssh_tunnel >= 0 AND use_ssh_tunnel <= 1'),
+        nullable=False
+    )
+    tunnel_host = db.Column(db.String(128), nullable=True)
+    tunnel_port = db.Column(
+        db.Integer(),
+        db.CheckConstraint('port <= 65534'),
+        nullable=True)
+    tunnel_username = db.Column(db.String(64), nullable=True)
+    tunnel_authentication = db.Column(
+        db.Integer(),
+        db.CheckConstraint('tunnel_authentication >= 0 AND '
+                           'tunnel_authentication <= 1'),
+        nullable=False
+    )
+    tunnel_identity_file = db.Column(db.String(64), nullable=True)
+    tunnel_password = db.Column(db.String(64), nullable=True)
+    shared = db.Column(db.Boolean(), nullable=False)
diff --git a/web/pgadmin/preferences/__init__.py b/web/pgadmin/preferences/__init__.py
index 6212f63f8..db11d1d09 100644
--- a/web/pgadmin/preferences/__init__.py
+++ b/web/pgadmin/preferences/__init__.py
@@ -23,6 +23,7 @@ from pgadmin.utils.ajax import success_return, \
 from pgadmin.utils.menu import MenuItem
 from pgadmin.utils.preferences import Preferences
 from pgadmin.utils.constants import MIMETYPE_APP_JS
+from pgadmin.browser.server_groups import ServerGroupModule as sgm
 
 MODULE_NAME = 'preferences'
 
@@ -203,6 +204,7 @@ def save(pid):
 
     res, msg = Preferences.save(
         data['mid'], data['category_id'], data['id'], data['value'])
+    sgm.get_nodes(sgm)
 
     if not res:
         return internal_server_error(errormsg=msg)
diff --git a/web/pgadmin/preferences/static/js/preferences.js b/web/pgadmin/preferences/static/js/preferences.js
index 62eda7aa6..6c35cf0d6 100644
--- a/web/pgadmin/preferences/static/js/preferences.js
+++ b/web/pgadmin/preferences/static/js/preferences.js
@@ -463,6 +463,7 @@ define('pgadmin.preferences', [
             }
 
             if (e.button.text == gettext('Save')) {
+              debugger;
               let requires_refresh = false;
               preferences.updateAll();
 
@@ -477,6 +478,29 @@ define('pgadmin.preferences', [
                 if(pref.name == 'theme') {
                   requires_refresh = true;
                 }
+
+                if(pref.name == 'hide_shared_server') {
+                  Alertify.confirm(
+                    gettext('Browser tree refresh required'),
+                    gettext('A browser tree refresh is required. Do you wish to refresh the tree?'),
+                    function() {
+                      pgAdmin.Browser.tree.destroy({
+                        success: function() {
+                          pgAdmin.Browser.initializeBrowserTree(pgAdmin.Browser);
+                          return true;
+                        },
+                      });
+                    },
+                    function() {
+                      preferences.reset();
+                      changed = {};
+                      return true;
+                    }
+                  ).set('labels', {
+                    ok: gettext('Refresh'),
+                    cancel: gettext('Later'),
+                  });
+                }
               });
 
               if(requires_refresh) {
diff --git a/web/pgadmin/tools/schema_diff/__init__.py b/web/pgadmin/tools/schema_diff/__init__.py
index a325e9a01..babb05888 100644
--- a/web/pgadmin/tools/schema_diff/__init__.py
+++ b/web/pgadmin/tools/schema_diff/__init__.py
@@ -20,13 +20,14 @@ from flask_babelex import gettext
 from pgadmin.utils import PgAdminModule
 from pgadmin.utils.ajax import make_json_response, bad_request, \
     make_response as ajax_response, internal_server_error
-from pgadmin.model import Server
+from pgadmin.model import Server, SharedServer
 from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
 from pgadmin.tools.schema_diff.model import SchemaDiffModel
 from config import PG_DEFAULT_DRIVER
 from pgadmin.utils.driver import get_driver
 from pgadmin.utils.preferences import Preferences
 from pgadmin.utils.constants import PREF_LABEL_DISPLAY, MIMETYPE_APP_JS
+from sqlalchemy import or_
 
 MODULE_NAME = 'schema_diff'
 
@@ -270,6 +271,7 @@ def servers():
     server id.
     """
     res = {}
+    auto_detected_server = None
     try:
         """Return a JSON document listing the server groups for the user"""
         driver = get_driver(PG_DEFAULT_DRIVER)
@@ -277,7 +279,19 @@ def servers():
         from pgadmin.browser.server_groups.servers import\
             server_icon_and_background
 
-        for server in Server.query.filter_by(user_id=current_user.id):
+        for server in Server.query.filter(
+                or_(Server.user_id == current_user.id, Server.shared)):
+
+            shared_server = SharedServer.query.filter_by(
+                name=server.name, user_id=current_user.id,
+                servergroup_id=server.servergroup_id).first()
+
+            if server.discovery_id:
+                auto_detected_server = server.name
+
+            if shared_server and shared_server.name == auto_detected_server:
+                continue
+
             manager = driver.connection_manager(server.id)
             conn = manager.connection()
             connected = conn.connected()
diff --git a/web/pgadmin/tools/schema_diff/static/js/schema_diff.backform.js b/web/pgadmin/tools/schema_diff/static/js/schema_diff.backform.js
index 55ef1e5f4..1b095db98 100644
--- a/web/pgadmin/tools/schema_diff/static/js/schema_diff.backform.js
+++ b/web/pgadmin/tools/schema_diff/static/js/schema_diff.backform.js
@@ -175,7 +175,7 @@ let SchemaDiffSelect2Control =
       let span = this.$el.find('.select2-selection .select2-selection__rendered span.wcTabIcon'),
         selSpan = this.$el.find('option:selected');
 
-      if (span.hasClass('icon-server-not-connected')) {
+      if (span.hasClass('icon-server-not-connected') || span.hasClass('icon-shared-server-not-connected')) {
         let icon = (data.icon) ? data.icon : 'icon-pg';
         span.removeClass('icon-server-not-connected');
         span.addClass(icon);
diff --git a/web/pgadmin/utils/driver/psycopg2/server_manager.py b/web/pgadmin/utils/driver/psycopg2/server_manager.py
index c35a3726f..6d3ce25ff 100644
--- a/web/pgadmin/utils/driver/psycopg2/server_manager.py
+++ b/web/pgadmin/utils/driver/psycopg2/server_manager.py
@@ -66,6 +66,7 @@ class ServerManager(object):
         self.hostaddr = server.hostaddr
         self.port = server.port
         self.db = server.maintenance_db
+        self.shared = server.shared
         self.did = None
         self.user = server.username
         self.password = server.password
diff --git a/web/regression/python_test_utils/test_utils.py b/web/regression/python_test_utils/test_utils.py
index 6d66af15a..022856c0c 100644
--- a/web/regression/python_test_utils/test_utils.py
+++ b/web/regression/python_test_utils/test_utils.py
@@ -36,6 +36,8 @@ from regression import test_setup
 
 from pgadmin.utils.preferences import Preferences
 
+from functools import wraps
+
 CURRENT_PATH = os.path.abspath(os.path.join(os.path.dirname(
     os.path.realpath(__file__)), "../"))
 
@@ -1598,3 +1600,107 @@ def get_selenoid_browsers_list(arguments):
         list_of_browsers = test_setup.config_data['selenoid_config'][
             'browsers_list']
     return list_of_browsers
+
+
+def login_using_user_account(tester):
+    """
+    This function login the test client username and password
+    :param tester: test client
+    :type tester: flask test client object
+    :return: None
+    """
+    username = tester.test_config_data['login_username']
+    password = tester.test_config_data['login_password']
+    response = tester.login(username, password)
+
+    if response.status_code != 302:
+        print("Unable to login test client, email and password not found.",
+              file=sys.stderr)
+        sys.exit(1)
+
+
+def logout_tester_account(tester):
+    """
+    This function logout the test account
+    :param tester: test client
+    :type tester: flask test client object
+    :return: None
+    """
+    tester.logout()
+
+
+def create_user(user_details):
+    try:
+        conn = sqlite3.connect(config.TEST_SQLITE_PATH)
+        # Create the server
+        cur = conn.cursor()
+        user_details = (
+            user_details['login_username'], user_details['login_username'],
+            user_details['login_password'], 1)
+
+        cur.execute(
+            'select * from user where username = "%s"' % user_details[0])
+        user = cur.fetchone()
+        if user is None:
+            cur.execute('INSERT INTO user (username, email, password, active) '
+                        'VALUES (?,?,?,?)', user_details)
+            user_id = cur.lastrowid
+            conn.commit()
+        else:
+            user_id = user[0]
+        conn.close()
+
+        return user_id
+    except Exception as exception:
+        traceback.print_exc(file=sys.stderr)
+        raise ("Error while creating server. %s" % exception)
+
+
+def get_test_user(self, user_details,
+                  is_api=True, create_conn=True):
+    if user_details is None:
+        return None, None
+
+    if is_api is True:
+
+        # Create test_client for this user, and login through it.
+        test_client = self.app.test_client()
+        user = create_user(user_details)
+        if user is not None:
+            test_client.test_config_data = dict({
+                "login_username": user_details['login_username'],
+                "login_password": user_details['login_password']
+            })
+        else:
+            return "User not created"
+        login_using_user_account(test_client)
+        user = test_client
+
+    return user
+
+
+def create_user_wise_test_client(user):
+    """
+    This function creates new test client and pem database connection as per
+    provided user and execute the test cases.
+    :return: None
+    """
+
+    def multi_user_decorator(func):
+        @wraps(func)
+        def wrapper(self, *args, **kwargs):
+            main_tester = self.__class__.tester
+            try:
+                # Login with non-admin_user
+                test_user = get_test_user(self, user)
+                self.setTestClient(test_user)
+
+                # Call 'runTest' with new test client
+                func(self, *args, **kwargs)
+            finally:
+                # Restore the original user and driver
+                self.__class__.tester = main_tester
+
+        return wrapper
+
+    return multi_user_decorator
diff --git a/web/regression/test_config.json.in b/web/regression/test_config.json.in
index 690f46c1c..393be6a84 100644
--- a/web/regression/test_config.json.in
+++ b/web/regression/test_config.json.in
@@ -11,6 +11,11 @@
     "login_password": "PASSWORD",
     "login_username": "[email protected]"
   },
+  "pgAdmin4_test_non_admin_credentials": {
+    "new_password": "NEWPASSWORD",
+    "login_password": "PASSWORD",
+    "login_username": "[email protected]"
+  },
   "pgAdmin4_ldap_credentials": {
     "login_password": "PASSWORD",
     "login_username": "USERNAME"
diff --git a/web/setup.py b/web/setup.py
index e9c42decd..cfdf2045a 100644
--- a/web/setup.py
+++ b/web/setup.py
@@ -110,6 +110,7 @@ def dump_servers(args):
                 add_value(attr_dict, "Role", server.role)
                 add_value(attr_dict, "SSLMode", server.ssl_mode)
                 add_value(attr_dict, "Comment", server.comment)
+                add_value(attr_dict, "Shared", server.shared)
                 add_value(attr_dict, "DBRestriction", server.db_res)
                 add_value(attr_dict, "PassFile", server.passfile)
                 add_value(attr_dict, "SSLCert", server.sslcert)
@@ -258,6 +259,14 @@ def load_servers(args):
         for server in data["Servers"]:
             obj = data["Servers"][server]
 
+            # Check if server is shared.Won't import if user is non-admin
+            if 'Shared' in obj \
+                and obj['Shared'] and \
+                    not user.has_role("Administrator"):
+                print("Can't import the server '%s' as it is shared " %
+                      obj["Name"])
+                continue
+
             # Get the group. Create if necessary
             group_id = next(
                 (g.id for g in groups if g.name == obj["Group"]), -1)


view thread (13+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected]
  Subject: Re: [pgAdmin][RM4979]: Configuration files for data sources or similar.
  In-Reply-To: <CAJ9T6StLHKviij7ZPXC36amgYy=tsMvKqW48oTxzUbkUaFx3gQ@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox