Added encrypted password in reverse engineered SQL for roles. Fixes #1974.

This commit is contained in:
Aditya Toshniwal 2019-10-23 13:16:36 +05:30 committed by Akshay Joshi
parent 5b438e13cc
commit 02642628b0
7 changed files with 53 additions and 29 deletions

View File

@ -9,6 +9,7 @@ This release contains a number of bug fixes and new features since the release o
New features New features
************ ************
| `Issue #1974 <https://redmine.postgresql.org/issues/1974>`_ - Added encrypted password in reverse engineered SQL for roles.
Housekeeping Housekeeping
************ ************

View File

@ -735,9 +735,10 @@ rolmembership:{
@check_precondition() @check_precondition()
def sql(self, gid, sid, rid): def sql(self, gid, sid, rid):
show_password = self.conn.manager.user_info['is_superuser']
status, res = self.conn.execute_scalar( status, res = self.conn.execute_scalar(
render_template( render_template(
self.sql_path + 'sql.sql' self.sql_path + 'sql.sql', show_password=show_password
), ),
dict({'rid': rid}) dict({'rid': rid})
) )

View File

@ -8,7 +8,6 @@ FROM
pg_catalog.quote_ident(rolname) || E';\n\nCREATE ROLE ' || pg_catalog.quote_ident(rolname) || E';\n\nCREATE ROLE ' ||
pg_catalog.quote_ident(rolname) || E' WITH\n ' || pg_catalog.quote_ident(rolname) || E' WITH\n ' ||
CASE WHEN rolcanlogin THEN 'LOGIN' ELSE 'NOLOGIN' END || E'\n ' || CASE WHEN rolcanlogin THEN 'LOGIN' ELSE 'NOLOGIN' END || E'\n ' ||
CASE WHEN rolcanlogin AND rolpassword LIKE 'md5%%' THEN 'ENCRYPTED PASSWORD ' || quote_literal(rolpassword) || E'\n ' ELSE '' END ||
CASE WHEN rolsuper THEN 'SUPERUSER' ELSE 'NOSUPERUSER' END || E'\n ' || CASE WHEN rolsuper THEN 'SUPERUSER' ELSE 'NOSUPERUSER' END || E'\n ' ||
CASE WHEN rolinherit THEN 'INHERIT' ELSE 'NOINHERIT' END || E'\n ' || CASE WHEN rolinherit THEN 'INHERIT' ELSE 'NOINHERIT' END || E'\n ' ||
CASE WHEN rolcreatedb THEN 'CREATEDB' ELSE 'NOCREATEDB' END || E'\n ' || CASE WHEN rolcreatedb THEN 'CREATEDB' ELSE 'NOCREATEDB' END || E'\n ' ||
@ -16,6 +15,12 @@ FROM
-- PostgreSQL >= 9.1 -- PostgreSQL >= 9.1
CASE WHEN rolreplication THEN 'REPLICATION' ELSE 'NOREPLICATION' END || CASE WHEN rolreplication THEN 'REPLICATION' ELSE 'NOREPLICATION' END ||
CASE WHEN rolconnlimit > 0 THEN E'\n CONNECTION LIMIT ' || rolconnlimit ELSE '' END || CASE WHEN rolconnlimit > 0 THEN E'\n CONNECTION LIMIT ' || rolconnlimit ELSE '' END ||
{% if show_password %}
(SELECT CASE
WHEN (rolpassword LIKE 'md5%%' or rolpassword LIKE 'SCRAM%%') THEN E'\n ENCRYPTED PASSWORD ''' || rolpassword || ''''
WHEN rolpassword IS NOT NULL THEN E'\n PASSWORD ''' || rolpassword || ''''
ELSE '' END FROM pg_authid au WHERE au.oid=r.oid) ||
{% endif %}
CASE WHEN rolvaliduntil IS NOT NULL THEN E'\n VALID UNTIL ' || quote_literal(rolvaliduntil::text) ELSE '' END || ';' AS sql CASE WHEN rolvaliduntil IS NOT NULL THEN E'\n VALID UNTIL ' || quote_literal(rolvaliduntil::text) ELSE '' END || ';' AS sql
FROM FROM
pg_roles r pg_roles r

View File

@ -9,6 +9,7 @@ CREATE ROLE "Role2_$%{}[]()&*^!@""'`\/#" WITH
CREATEROLE CREATEROLE
NOREPLICATION NOREPLICATION
CONNECTION LIMIT 100 CONNECTION LIMIT 100
ENCRYPTED PASSWORD '<PASSWORD>'
VALID UNTIL '<TIMESTAMPTZ>'; VALID UNTIL '<TIMESTAMPTZ>';
ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" IN DATABASE postgres SET application_name TO 'pg4'; ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" IN DATABASE postgres SET application_name TO 'pg4';

View File

@ -9,6 +9,7 @@ CREATE ROLE "Role2_$%{}[]()&*^!@""'`\/#" WITH
NOCREATEROLE NOCREATEROLE
NOREPLICATION NOREPLICATION
CONNECTION LIMIT 100 CONNECTION LIMIT 100
ENCRYPTED PASSWORD '<PASSWORD>'
VALID UNTIL '<TIMESTAMPTZ>'; VALID UNTIL '<TIMESTAMPTZ>';
ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" IN DATABASE postgres SET application_name TO 'pg4'; ALTER ROLE "Role2_$%{}[]()&*^!@""'`\/#" IN DATABASE postgres SET application_name TO 'pg4';

View File

@ -65,7 +65,8 @@
}, },
"expected_sql_file": "alter_role_options.sql", "expected_sql_file": "alter_role_options.sql",
"expected_msql_file": "alter_role_options.msql", "expected_msql_file": "alter_role_options.msql",
"convert_timestamp_columns": ["rolvaliduntil"] "convert_timestamp_columns": ["rolvaliduntil"],
"replace_password": true
}, },
{ {
"type": "delete", "type": "delete",
@ -138,7 +139,8 @@
}, },
"expected_sql_file": "alter_login_role_options.sql", "expected_sql_file": "alter_login_role_options.sql",
"expected_msql_file": "alter_login_role_options.msql", "expected_msql_file": "alter_login_role_options.msql",
"convert_timestamp_columns": ["rolvaliduntil"] "convert_timestamp_columns": ["rolvaliduntil"],
"replace_password": true
}, },
{ {
"type": "delete", "type": "delete",

View File

@ -9,6 +9,7 @@
from __future__ import print_function from __future__ import print_function
import json import json
import os import os
import re
import traceback import traceback
from flask import url_for from flask import url_for
import regression import regression
@ -103,7 +104,8 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
# while running the test cases # while running the test cases
self.JSON_PLACEHOLDERS = {'schema_id': '<SCHEMA_ID>', self.JSON_PLACEHOLDERS = {'schema_id': '<SCHEMA_ID>',
'owner': '<OWNER>', 'owner': '<OWNER>',
'timestamptz': '<TIMESTAMPTZ>'} 'timestamptz': '<TIMESTAMPTZ>',
'password': '<PASSWORD>'}
resql_module_list = create_resql_module_list( resql_module_list = create_resql_module_list(
BaseTestGenerator.re_sql_module_list, BaseTestGenerator.re_sql_module_list,
@ -416,14 +418,7 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
fp = open(output_file, "r") fp = open(output_file, "r")
# Used rstrip to remove trailing \n # Used rstrip to remove trailing \n
sql = fp.read().rstrip() sql = fp.read().rstrip()
# Replace place holder <owner> with the current username sql = self.preprocess_expected_sql(scenario, sql, resp_sql)
# used to connect to the database
if 'username' in self.server:
sql = sql.replace(self.JSON_PLACEHOLDERS['owner'],
self.server['username'])
# Convert timestamp with timezone from json file to the
# database server's correct timestamp
sql = self.convert_timestamptz(scenario, sql)
try: try:
self.assertEquals(sql, resp_sql) self.assertEquals(sql, resp_sql)
except Exception as e: except Exception as e:
@ -477,14 +472,7 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
fp = open(output_file, "r") fp = open(output_file, "r")
# Used rstrip to remove trailing \n # Used rstrip to remove trailing \n
sql = fp.read().rstrip() sql = fp.read().rstrip()
# Replace place holder <owner> with the current username sql = self.preprocess_expected_sql(scenario, sql, resp_sql)
# used to connect to the database
if 'username' in self.server:
sql = sql.replace(self.JSON_PLACEHOLDERS['owner'],
self.server['username'])
# Convert timestamp with timezone from json file to the
# database server's correct timestamp
sql = self.convert_timestamptz(scenario, sql)
try: try:
self.assertEquals(sql, resp_sql) self.assertEquals(sql, resp_sql)
except Exception as e: except Exception as e:
@ -500,14 +488,7 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
return False return False
elif 'expected_sql' in scenario: elif 'expected_sql' in scenario:
exp_sql = scenario['expected_sql'] exp_sql = scenario['expected_sql']
# Replace place holder <owner> with the current username exp_sql = self.preprocess_expected_sql(scenario, exp_sql, resp_sql)
# used to connect to the database
if 'username' in self.server:
exp_sql = exp_sql.replace(self.JSON_PLACEHOLDERS['owner'],
self.server['username'])
# Convert timestamp with timezone from json file to the
# database server's correct timestamp
sql = self.convert_timestamptz(scenario, exp_sql)
try: try:
self.assertEquals(exp_sql, resp_sql) self.assertEquals(exp_sql, resp_sql)
except Exception as e: except Exception as e:
@ -643,6 +624,38 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
return data return data
def preprocess_expected_sql(self, scenario, sql, resp_sql):
"""
This function preprocesses expected sql before comparing
it with response sql.
:param data: sql
:param data: resp_sql
:return:
"""
# Replace place holder <owner> with the current username
# used to connect to the database
if 'username' in self.server:
sql = sql.replace(self.JSON_PLACEHOLDERS['owner'],
self.server['username'])
# Convert timestamp with timezone from json file to the
# database server's correct timestamp
sql = self.convert_timestamptz(scenario, sql)
# extract password fields from response and replace in expected
# to match the response
if 'replace_password' in scenario:
password = ''
for line in resp_sql.split('\n'):
if 'PASSWORD' in line:
found = re.search("'([\w\W]*)'", line)
if found:
password = found.groups(0)[0]
break
sql = sql.replace(self.JSON_PLACEHOLDERS['password'], password)
return sql
def replace_placeholder_with_id(self, value): def replace_placeholder_with_id(self, value):
""" """
This function is used to replace the place holder with id. This function is used to replace the place holder with id.