diff --git a/web/pgadmin/browser/server_groups/servers/roles/__init__.py b/web/pgadmin/browser/server_groups/servers/roles/__init__.py index a5db5c9bb..c976ded38 100644 --- a/web/pgadmin/browser/server_groups/servers/roles/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/roles/__init__.py @@ -111,35 +111,75 @@ class RoleView(PGChildNodeView): 'variables': [{'get': 'variables'}], }) - @staticmethod - def _get_request_data(): + def _validate_input_dict_for_new(self, data, req_keys): """ - Get data from client request. + This functions validates the input dict and check for required + keys in the dict when creating a new object + :param data: input dict + :param req_keys: required keys + :return: Valid or Invalid """ - if request.data: - data = json.loads(request.data, encoding='utf-8') - else: - data = dict() - req = request.args or request.form + if type(data) != list: + return False - for key in req: + for item in data: + if type(item) != dict: + return False - val = req[key] - if key in [ - u'rolcanlogin', u'rolsuper', u'rolcreatedb', - u'rolcreaterole', u'rolinherit', u'rolreplication', - u'rolcatupdate', u'variables', u'rolmembership', - u'seclabels' - ]: - data[key] = json.loads(val, encoding='utf-8') - else: - data[key] = val - return data + for a_key in req_keys: + if a_key not in item: + return False - @staticmethod - def _check_roleconnlimit(data): + return True + + def _validate_input_dict_for_update( + self, data, req_add_keys, req_delete_keys): + """ + This functions validates the input dict and check for required + keys in the dict when updating an existing object + :param data: input dict + :param req_add_keys: required keys when adding, updating + :param req_delete_keys: required keys when deleting + :return: Valid or Invalid + """ + if type(data) != dict: + return False + + for op in [u'added', u'deleted', u'changed']: + op_data = data.get(op, []) + check_keys = req_add_keys \ + if op in [u'added', u'changed'] else req_delete_keys + if not self._validate_input_dict_for_new(op_data, check_keys): + return False + + return True + + def _validate_rolvaliduntil(self, data): + """ + Validate the rolvaliduntil in input data dict + :param data: role data + :return: valid or invalid message """ - Check connection limit for role. + if u'rolvaliduntil' in data: + # Make date explicit so that it works with every + # postgres database datestyle format + try: + if data[u'rolvaliduntil'] is not None and \ + data[u'rolvaliduntil'] != '' and \ + len(data[u'rolvaliduntil']) > 0: + data[u'rolvaliduntil'] = dateutil_parser.parse( + data[u'rolvaliduntil'] + ).isoformat() + except Exception: + return _("Date format is invalid.") + + return None + + def _validate_rolconnlimit(self, data): + """ + Validate the rolconnlimit data dict + :param data: role data + :return: valid or invalid message """ if u'rolconnlimit' in data: # If roleconnlimit is empty string then set it to -1 @@ -150,165 +190,62 @@ class RoleView(PGChildNodeView): data[u'rolconnlimit'] = int(data[u'rolconnlimit']) if type(data[u'rolconnlimit']) != int or \ data[u'rolconnlimit'] < -1: - return True, "Connection limit must be an integer value " \ - "or equal to -1." - - return False, '' + return _("Connection limit must be an integer value " + "or equal to -1.") + return None - @staticmethod - def _check_role(data): + def _process_rolemembership(self, id, data): """ - Check user role + Process the input rolemembership list to appropriate keys + :param id: id of role + :param data: input role data """ - msg = _(""" - Role membership information must be passed as an array of JSON objects - in the following format: - - rolmembership:[{ - role: [rolename], - admin: True/False - }, - ... - ]""") - if type(data[u'rolmembership']) != list: - return True, msg - - data[u'members'] = [] - data[u'admins'] = [] - - for r in data[u'rolmembership']: - if type(r) != dict or u'role' not in r or \ - u'admin' not in r: - return True, msg - else: - if r[u'admin']: - data[u'admins'].append(r[u'role']) - else: - data[u'members'].append(r[u'role']) - return False, '' - - @staticmethod - def _check_precondition_added(data): + def _part_dict_list(dict_list, condition, list_key=None): + ret_val = [] + for d in dict_list: + if condition(d): + ret_val.append(d[list_key]) + + return ret_val + + if id == -1: + data[u'members'] = [] + data[u'admins'] = [] + + data[u'admins'] = _part_dict_list( + data[u'rolmembership'], lambda d: d[u'admin'], u'role') + data[u'members'] = _part_dict_list( + data[u'rolmembership'], lambda d: not d[u'admin'], u'role') + else: + data[u'admins'] = _part_dict_list( + data[u'rolmembership'].get(u'added', []), + lambda d: d[u'admin'], u'role') + data[u'members'] = _part_dict_list( + data[u'rolmembership'].get(u'added', []), + lambda d: not d[u'admin'], u'role') + + data[u'admins'].extend(_part_dict_list( + data[u'rolmembership'].get(u'changed', []), + lambda d: d[u'admin'], u'role')) + data[u'revoked_admins'] = _part_dict_list( + data[u'rolmembership'].get(u'changed', []), + lambda d: not d[u'admin'], u'role') + + data[u'revoked'] = _part_dict_list( + data[u'rolmembership'].get(u'deleted', []), + lambda _: True, u'role') + + def _validate_rolemembership(self, id, data): """ - Check for pre condition for added + Validate the rolmembership data dict + :param data: role data + :return: valid or invalid message """ - if u'added' in data[u'rolmembership']: - roles = (data[u'rolmembership'])[u'added'] - - if type(roles) != list: - return True - - for r in roles: - if type(r) != dict or \ - u'role' not in r or \ - u'admin' not in r: - return True - - if r[u'admin']: - data[u'admins'].append(r[u'role']) - else: - data[u'members'].append(r[u'role']) - return False - - @staticmethod - def _check_precondition_deleted(data): - if u'deleted' in data[u'rolmembership']: - roles = (data[u'rolmembership'])[u'deleted'] - - if type(roles) != list: - return True - - for r in roles: - if type(r) != dict or u'role' not in r: - return True - - data[u'revoked'].append(r[u'role']) - - return False - - @staticmethod - def _check_precondition_change(data): - if u'changed' in data[u'rolmembership']: - roles = (data[u'rolmembership'])[u'changed'] - - if type(roles) != list: - return True + if u'rolmembership' not in data: + return None - for r in roles: - if type(r) != dict or \ - u'role' not in r or \ - u'admin' not in r: - return True - - if not r[u'admin']: - data[u'revoked_admins'].append(r[u'role']) - else: - data[u'admins'].append(r[u'role']) - - return False - - def validate_request(f): - @wraps(f) - def wrap(self, **kwargs): - - data = None - if request.data: - data = json.loads(request.data, encoding='utf-8') - else: - data = dict() - req = request.args or request.form - - for key in req: - - val = req[key] - if key in [ - u'rolcanlogin', u'rolsuper', u'rolcreatedb', - u'rolcreaterole', u'rolinherit', u'rolreplication', - u'rolcatupdate', u'variables', u'rolmembership', - u'seclabels' - ]: - data[key] = json.loads(val, encoding='utf-8') - else: - data[key] = val - - if (u'rid' not in kwargs or kwargs['rid'] == -1) and \ - u'rolname' not in data: - return precondition_required( - _("Name must be specified.") - ) - - if u'rolvaliduntil' in data: - # Make date explicit so that it works with every - # postgres database datestyle format - try: - if data[u'rolvaliduntil'] is not None and \ - data[u'rolvaliduntil'] != '' and \ - len(data[u'rolvaliduntil']) > 0: - data[u'rolvaliduntil'] = dateutil_parser.parse( - data[u'rolvaliduntil'] - ).isoformat() - except Exception: - return precondition_required( - _("Date format is invalid.") - ) - - if u'rolconnlimit' in data: - # If roleconnlimit is empty string then set it to -1 - if data[u'rolconnlimit'] == '': - data[u'rolconnlimit'] = -1 - - if data[u'rolconnlimit'] is not None: - data[u'rolconnlimit'] = int(data[u'rolconnlimit']) - if type(data[u'rolconnlimit']) != int or \ - data[u'rolconnlimit'] < -1: - return precondition_required( - _("Connection limit must be an integer value " - "or equal to -1.") - ) - - if u'rolmembership' in data: - if u'rid' not in kwargs or kwargs['rid'] == -1: - msg = _(""" + if id == -1: + msg = _(""" Role membership information must be passed as an array of JSON objects in the following format: @@ -318,23 +255,15 @@ rolmembership:[{ }, ... ]""") - if type(data[u'rolmembership']) != list: - return precondition_required(msg) - - data[u'members'] = [] - data[u'admins'] = [] - - for r in data[u'rolmembership']: - if type(r) != dict or u'role' not in r or \ - u'admin' not in r: - return precondition_required(msg) - else: - if r[u'admin']: - data[u'admins'].append(r[u'role']) - else: - data[u'members'].append(r[u'role']) - else: - msg = _(""" + + if not self._validate_input_dict_for_new( + data[u'rolmembership'], [u'role', u'admin']): + return msg + + self._process_rolemembership(id, data) + return None + + msg = _(""" Role membership information must be passed as a string representing an array of JSON objects in the following format: rolmembership:{ @@ -357,63 +286,24 @@ rolmembership:{ ... ] """) - if type(data[u'rolmembership']) != dict: - return precondition_required(msg) - - data[u'members'] = [] - data[u'admins'] = [] - data[u'revoked_admins'] = [] - data[u'revoked'] = [] - - if u'added' in data[u'rolmembership']: - roles = (data[u'rolmembership'])[u'added'] - - if type(roles) != list: - return precondition_required(msg) + if not self._validate_input_dict_for_update( + data[u'rolmembership'], [u'role', u'admin'], [u'role']): + return msg - for r in roles: - if type(r) != dict or \ - u'role' not in r or \ - u'admin' not in r: - return precondition_required(msg) + self._process_rolemembership(id, data) + return None - if r[u'admin']: - data[u'admins'].append(r[u'role']) - else: - data[u'members'].append(r[u'role']) - - if u'deleted' in data[u'rolmembership']: - roles = (data[u'rolmembership'])[u'deleted'] - - if type(roles) != list: - return precondition_required(msg) - - for r in roles: - if type(r) != dict or u'role' not in r: - return precondition_required(msg) - - data[u'revoked'].append(r[u'role']) - - if u'changed' in data[u'rolmembership']: - roles = (data[u'rolmembership'])[u'changed'] - - if type(roles) != list: - return precondition_required(msg) - - for r in roles: - if type(r) != dict or \ - u'role' not in r or \ - u'admin' not in r: - return precondition_required(msg) - - if not r[u'admin']: - data[u'revoked_admins'].append(r[u'role']) - else: - data[u'admins'].append(r[u'role']) + def _validate_seclabels(self, id, data): + """ + Validate the seclabels data dict + :param data: role data + :return: valid or invalid message + """ + if u'seclabels' not in data or self.manager.version < 90200: + return None - if self.manager.version >= 90200 and u'seclabels' in data: - if u'rid' not in kwargs or kwargs['rid'] == -1: - msg = _(""" + if id == -1: + msg = _(""" Security Label must be passed as an array of JSON objects in the following format: seclabels:[{ @@ -422,16 +312,13 @@ seclabels:[{ }, ... ]""") - if type(data[u'seclabels']) != list: - return precondition_required(msg) - - for s in data[u'seclabels']: - if type(s) != dict or \ - u'provider' not in s or \ - u'label' not in s: - return precondition_required(msg) - else: - msg = _(""" + if not self._validate_input_dict_for_new( + data[u'seclabels'], [u'provider', u'label']): + return msg + + return None + + msg = _(""" Security Label must be passed as an array of JSON objects in the following format: seclabels:{ @@ -454,125 +341,129 @@ seclabels:{ ... ] """) - seclabels = data[u'seclabels'] - if type(seclabels) != dict: - return precondition_required(msg) - - if u'added' in seclabels: - new_seclabels = seclabels[u'added'] - - if type(new_seclabels) != list: - return precondition_required(msg) - - for s in new_seclabels: - if type(s) != dict or \ - u'provider' not in s or \ - u'label' not in s: - return precondition_required(msg) - - if u'deleted' in seclabels: - removed_seclabels = seclabels[u'deleted'] - - if type(removed_seclabels) != list: - return precondition_required(msg) - - for s in removed_seclabels: - if (type(s) != dict or u'provider' not in s): - return precondition_required(msg) - - if u'changed' in seclabels: - changed_seclabels = seclabels[u'deleted'] + if not self._validate_input_dict_for_update( + data[u'seclabels'], [u'provider', u'label'], [u'provider']): + return msg - if type(changed_seclabels) != list: - return precondition_required(msg) + return None - for s in changed_seclabels: - if type(s) != dict or \ - u'provider' not in s and \ - u'label' not in s: - return precondition_required(msg) + def _validate_variables(self, id, data): + """ + Validate the variables data dict + :param data: role data + :return: valid or invalid message + """ + if u'variables' not in data: + return None - if u'variables' in data: - if u'rid' not in kwargs or kwargs['rid'] == -1: - msg = _(""" + if id == -1: + msg = _(""" Configuration parameters/variables must be passed as an array of JSON objects in the following format in create mode: variables:[{ +database: or null, +name: , +value: +}, +... +]""") + if not self._validate_input_dict_for_new( + data[u'variables'], [u'name', u'value']): + return msg + + return None + + msg = _(""" +Configuration parameters/variables must be passed as an array of JSON objects +in the following format in update mode: +rolmembership:{ +'added': [{ database: or null, name: , value: }, ... -]""") - if type(data[u'variables']) != list: - return precondition_required(msg) - - for r in data[u'variables']: - if type(r) != dict or u'name' not in r or \ - u'value' not in r: - return precondition_required(msg) - else: - msg = _(""" -Configuration parameters/variables must be passed as an array of JSON objects -in the following format in update mode: -rolmembership:{ - 'added': [{ - database: or null, - name: , - value: - }, - ... - ], - 'deleted': [{ - database: or null, - name: , - value: - }, - ... - ], - 'updated': [{ - database: or null, - name: , - value: - }, - ... - ] + ], +'deleted': [{ + database: or null, + name: , + value: + }, + ... + ], +'updated': [{ + database: or null, + name: , + value: + }, + ... + ] """) - variables = data[u'variables'] - if type(variables) != dict: - return precondition_required(msg) + if not self._validate_input_dict_for_update( + data[u'variables'], [u'name', u'value'], [u'name']): + return msg + return None - if u'added' in variables: - new_vars = variables[u'added'] + def _validate_rolname(self, id, data): + """ + Validate the rolname data dict + :param data: role data + :return: valid or invalid message + """ + if (id == -1) and u'rolname' not in data: + return precondition_required( + _("Name must be specified.") + ) + return None - if type(new_vars) != list: - return precondition_required(msg) + def validate_request(f): + @wraps(f) + def wrap(self, **kwargs): + if request.data: + data = json.loads(request.data, encoding='utf-8') + else: + data = dict() + req = request.args or request.form - for v in new_vars: - if type(v) != dict or u'name' not in v or \ - u'value' not in v: - return precondition_required(msg) + for key in req: + + val = req[key] + if key in [ + u'rolcanlogin', u'rolsuper', u'rolcreatedb', + u'rolcreaterole', u'rolinherit', u'rolreplication', + u'rolcatupdate', u'variables', u'rolmembership', + u'seclabels' + ]: + data[key] = json.loads(val, encoding='utf-8') + else: + data[key] = val - if u'deleted' in variables: - delete_vars = variables[u'deleted'] + invalid_msg = self._validate_rolname(kwargs.get('rid', -1), data) + if invalid_msg is not None: + return precondition_required(invalid_msg) - if type(delete_vars) != list: - return precondition_required(msg) + invalid_msg = self._validate_rolvaliduntil(data) + if invalid_msg is not None: + return precondition_required(invalid_msg) - for v in delete_vars: - if type(v) != dict or u'name' not in v: - return precondition_required(msg) + invalid_msg = self._validate_rolconnlimit(data) + if invalid_msg is not None: + return precondition_required(invalid_msg) - if u'changed' in variables: - new_vars = variables[u'changed'] + invalid_msg = self._validate_rolemembership( + kwargs.get(u'rid', -1), data) + if invalid_msg is not None: + return precondition_required(invalid_msg) - if type(new_vars) != list: - return precondition_required(msg) + invalid_msg = self._validate_seclabels( + kwargs.get(u'rid', -1), data) + if invalid_msg is not None: + return precondition_required(invalid_msg) - for v in new_vars: - if type(v) != dict or u'name' not in v or \ - u'value' not in v: - return precondition_required(msg) + invalid_msg = self._validate_variables( + kwargs.get(u'rid', -1), data) + if invalid_msg is not None: + return precondition_required(invalid_msg) self.request = data @@ -1090,7 +981,7 @@ rolmembership:{ ) @staticmethod - def _handel_dependents_type(types, type_str, type_name, rel_name, row): + def _handle_dependents_type(types, type_str, type_name, rel_name, row): if types[type_str[0]] is None: if type_str[0] == 'i': type_name = 'index' @@ -1104,7 +995,7 @@ rolmembership:{ return type_name, rel_name @staticmethod - def _handel_dependents_data(result, types, dependents, db_row): + def _handle_dependents_data(result, types, dependents, db_row): for row in result['rows']: rel_name = row['nspname'] if rel_name is not None: @@ -1123,7 +1014,7 @@ rolmembership:{ if type_str[0] in types: # if type is present in the types dictionary, but it's # value is None then it requires special handling. - type_name, rel_name = RoleView._handel_dependents_type( + type_name, rel_name = RoleView._handle_dependents_type( types, type_str, type_name, rel_name, row) else: continue @@ -1149,7 +1040,7 @@ rolmembership:{ if not status: current_app.logger.error(result) - RoleView._handel_dependents_data(result, types, dependents, db_row) + RoleView._handle_dependents_data(result, types, dependents, db_row) @staticmethod def _release_connection(is_connected, manager, db_row): diff --git a/web/pgadmin/browser/server_groups/servers/roles/templates/roles/sql/9.4_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/roles/templates/roles/sql/9.4_plus/properties.sql index 1ee0b6ce7..4699e10da 100644 --- a/web/pgadmin/browser/server_groups/servers/roles/templates/roles/sql/9.4_plus/properties.sql +++ b/web/pgadmin/browser/server_groups/servers/roles/templates/roles/sql/9.4_plus/properties.sql @@ -7,6 +7,7 @@ SELECT FROM (SELECT * FROM pg_auth_members WHERE member = r.oid) am LEFT JOIN pg_catalog.pg_roles rm ON (rm.oid = am.roleid) + ORDER BY rm.rolname ) AS rolmembership, (SELECT array_agg(provider || '=' || label) FROM pg_shseclabel sl1 WHERE sl1.objoid=r.oid) AS seclabels FROM diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options.msql b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options1.msql similarity index 58% rename from web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options.msql rename to web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options1.msql index 1a196a375..b6864d79d 100644 --- a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options.msql +++ b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options1.msql @@ -7,5 +7,7 @@ ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" VALID UNTIL '2050-01-01T00:00:00+05:30' PASSWORD 'xxxxxx'; +GRANT pg_signal_backend TO "Role2_$%{}[]()&*^!@""'`\/#" WITH ADMIN OPTION; +GRANT pg_monitor TO "Role2_$%{}[]()&*^!@""'`\/#"; ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" IN DATABASE postgres - SET application_name TO 'pg4'; \ No newline at end of file + SET application_name TO 'pg4'; diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options1.sql b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options1.sql new file mode 100644 index 000000000..9013abd9b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options1.sql @@ -0,0 +1,20 @@ +-- Role: "Role2_$%{}[]()&*^!@""'`\/#" +-- DROP ROLE "Role2_$%{}[]()&*^!@""'`\/#"; + +CREATE ROLE "Role2_$%{}[]()&*^!@""'`\/#" WITH + NOLOGIN + SUPERUSER + INHERIT + CREATEDB + NOCREATEROLE + NOREPLICATION + CONNECTION LIMIT 100 + ENCRYPTED PASSWORD '' + VALID UNTIL ''; + +GRANT pg_monitor TO "Role2_$%{}[]()&*^!@""'`\/#"; +GRANT pg_signal_backend TO "Role2_$%{}[]()&*^!@""'`\/#" WITH ADMIN OPTION; + +ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" IN DATABASE postgres SET application_name TO 'pg4'; + +COMMENT ON ROLE "Role2_$%{}[]()&*^!@""'`\/#" IS 'This is detailed description'; diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options2.msql b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options2.msql new file mode 100644 index 000000000..85a402a33 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options2.msql @@ -0,0 +1,2 @@ +REVOKE ADMIN OPTION FOR pg_signal_backend FROM "Role2_$%{}[]()&*^!@""'`\/#"; +GRANT pg_monitor TO "Role2_$%{}[]()&*^!@""'`\/#" WITH ADMIN OPTION; diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options2.sql b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options2.sql new file mode 100644 index 000000000..71dd6832b --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options2.sql @@ -0,0 +1,20 @@ +-- Role: "Role2_$%{}[]()&*^!@""'`\/#" +-- DROP ROLE "Role2_$%{}[]()&*^!@""'`\/#"; + +CREATE ROLE "Role2_$%{}[]()&*^!@""'`\/#" WITH + NOLOGIN + SUPERUSER + INHERIT + CREATEDB + NOCREATEROLE + NOREPLICATION + CONNECTION LIMIT 100 + ENCRYPTED PASSWORD '' + VALID UNTIL '2050-01-01 00:00:00+05:30'; + +GRANT pg_signal_backend TO "Role2_$%{}[]()&*^!@""'`\/#"; +GRANT pg_monitor TO "Role2_$%{}[]()&*^!@""'`\/#" WITH ADMIN OPTION; + +ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" IN DATABASE postgres SET application_name TO 'pg4'; + +COMMENT ON ROLE "Role2_$%{}[]()&*^!@""'`\/#" IS 'This is detailed description'; diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options3.msql b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options3.msql new file mode 100644 index 000000000..353fec735 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options3.msql @@ -0,0 +1 @@ +REVOKE pg_signal_backend FROM "Role2_$%{}[]()&*^!@""'`\/#"; diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options.sql b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options3.sql similarity index 79% rename from web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options.sql rename to web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options3.sql index 512322337..3e0f354c5 100644 --- a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options.sql +++ b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options3.sql @@ -10,7 +10,9 @@ CREATE ROLE "Role2_$%{}[]()&*^!@""'`\/#" WITH NOREPLICATION CONNECTION LIMIT 100 ENCRYPTED PASSWORD '' - VALID UNTIL ''; + VALID UNTIL '2050-01-01 00:00:00+05:30'; + +GRANT pg_monitor TO "Role2_$%{}[]()&*^!@""'`\/#" WITH ADMIN OPTION; ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" IN DATABASE postgres SET application_name TO 'pg4'; diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options4.msql b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options4.msql new file mode 100644 index 000000000..e18804b40 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options4.msql @@ -0,0 +1 @@ +GRANT pg_signal_backend, pg_monitor TO "Role2_$%{}[]()&*^!@""'`\/#" WITH ADMIN OPTION; diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options4.sql b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options4.sql new file mode 100644 index 000000000..b92fd5d3d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/alter_role_options4.sql @@ -0,0 +1,19 @@ +-- Role: "Role2_$%{}[]()&*^!@""'`\/#" +-- DROP ROLE "Role2_$%{}[]()&*^!@""'`\/#"; + +CREATE ROLE "Role2_$%{}[]()&*^!@""'`\/#" WITH + NOLOGIN + SUPERUSER + INHERIT + CREATEDB + NOCREATEROLE + NOREPLICATION + CONNECTION LIMIT 100 + ENCRYPTED PASSWORD '' + VALID UNTIL '2050-01-01 00:00:00+05:30'; + +GRANT pg_monitor, pg_signal_backend TO "Role2_$%{}[]()&*^!@""'`\/#" WITH ADMIN OPTION; + +ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" IN DATABASE postgres SET application_name TO 'pg4'; + +COMMENT ON ROLE "Role2_$%{}[]()&*^!@""'`\/#" IS 'This is detailed description'; diff --git a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/test.json b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/test.json index 2e184feef..46eb6cd30 100644 --- a/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/test.json +++ b/web/pgadmin/browser/server_groups/servers/roles/tests/9.4_plus/test.json @@ -50,7 +50,7 @@ }, { "type": "alter", - "name": "Alter Role options", + "name": "Alter Role options 1", "endpoint": "NODE-role.obj_id", "sql_endpoint": "NODE-role.sql_id", "msql_endpoint": "NODE-role.msql_id", @@ -61,10 +61,56 @@ "rolpassword": "abc123", "rolconnlimit": 100, "rolvaliduntil": "2050-01-01 00:00:00 +05:30", - "variables": { "added": [{"name":"application_name","value":"pg4","database":"postgres"}] } + "variables": { "added": [{"name":"application_name","value":"pg4","database":"postgres"}] }, + "rolmembership": { "added": [{"role": "pg_signal_backend", "admin": true}, {"role": "pg_monitor", "admin": false}] } + }, + "expected_sql_file": "alter_role_options1.sql", + "expected_msql_file": "alter_role_options1.msql", + "convert_timestamp_columns": ["rolvaliduntil"], + "replace_password": true + }, + { + "type": "alter", + "name": "Alter Role options 2", + "endpoint": "NODE-role.obj_id", + "sql_endpoint": "NODE-role.sql_id", + "msql_endpoint": "NODE-role.msql_id", + "data": { + "rolmembership": { "changed": [{"role": "pg_signal_backend", "admin": false}, {"role": "pg_monitor", "admin": true}] } + }, + "expected_sql_file": "alter_role_options2.sql", + "expected_msql_file": "alter_role_options2.msql", + "convert_timestamp_columns": ["rolvaliduntil"], + "replace_password": true + }, + { + "type": "alter", + "name": "Alter Role options 3", + "endpoint": "NODE-role.obj_id", + "sql_endpoint": "NODE-role.sql_id", + "msql_endpoint": "NODE-role.msql_id", + "data": { + "rolmembership": { "deleted": [{"role": "pg_signal_backend"}] } + }, + "expected_sql_file": "alter_role_options3.sql", + "expected_msql_file": "alter_role_options3.msql", + "convert_timestamp_columns": ["rolvaliduntil"], + "replace_password": true + }, + { + "type": "alter", + "name": "Alter Role options 4", + "endpoint": "NODE-role.obj_id", + "sql_endpoint": "NODE-role.sql_id", + "msql_endpoint": "NODE-role.msql_id", + "data": { + "rolmembership": { + "added": [{"role": "pg_signal_backend", "admin": true}], + "changed": [{"role": "pg_monitor", "admin": true}] + } }, - "expected_sql_file": "alter_role_options.sql", - "expected_msql_file": "alter_role_options.msql", + "expected_sql_file": "alter_role_options4.sql", + "expected_msql_file": "alter_role_options4.msql", "convert_timestamp_columns": ["rolvaliduntil"], "replace_password": true },