Jcrespo has submitted this change and it was merged.
Change subject: Create script to check that sanitarium filtering is working
......................................................................
Create script to check that sanitarium filtering is working
Bug: T150802
Change-Id: I66e43a1bc7554fd095ddda378a40856da115fdf9
---
A modules/role/files/mariadb/check_private_data.py
1 file changed, 181 insertions(+), 0 deletions(-)
Approvals:
Jcrespo: Looks good to me, approved
jenkins-bot: Verified
diff --git a/modules/role/files/mariadb/check_private_data.py
b/modules/role/files/mariadb/check_private_data.py
new file mode 100644
index 0000000..df4cac5
--- /dev/null
+++ b/modules/role/files/mariadb/check_private_data.py
@@ -0,0 +1,181 @@
+#!/usr/bin/python3
+
+import csv
+import os
+import re
+# requires python3-pymysql
+import pymysql
+
+# TODO:The following are hardcoded paths that will be parametrized
+# location where operations/wmf-config repo is synced
+mediawiki_config_path = '/usr/local/lib/mediawiki-config'
+# location where private_tables from realm.pp is synced to a text file
+private_tables_path = '/etc/mysql/private_tables.txt'
+# location where cols.txt from redactraton is synced to a csv (db, table, col)
+filtered_tables_path = '/etc/mysql/filtered_tables.txt'
+
+# full path for all.dblist
+all_dblist_path = os.path.join(mediawiki_config_path, 'dblists', 'all.dblist')
+# full path for private.dblist
+private_dblist_path = os.path.join(mediawiki_config_path, 'dblists',
+ 'private.dblist')
+# full path for deleted.dblist
+deleted_dblist_path = os.path.join(mediawiki_config_path, 'dblists',
+ 'deleted.dblist')
+
+# ignore the following system or ops-created databases:
+SYSTEM_DBS = ['mysql', 'information_schema', 'performance_schema', 'sys',
+ 'ops', 'percona']
+# ignore the following user-created databases:
+USER_DATABASES_REGEX = '^([su][0-9]+|p[0-9]+g[0-9]+)\_\_?'
+
+
+def parse_db_file(path, format='txt'):
+ """
+ load files to memory arrays
+ """
+ content = []
+ with open(path, 'r') as f:
+ if format == 'txt':
+ content = f.read().splitlines()
+ elif format == 'csv':
+ content = list(csv.reader(f))
+ return content
+
+
+def get_lists(all_dblist_path, private_dblist_path, deleted_dblist_path,
+ filtered_tables_path):
+ """
+ convert data from files to arrays
+ """
+ all_dblist = parse_db_file(all_dblist_path)
+ # we consider deleted wikis a private, too, for filtering purposes
+ private_dblist = (parse_db_file(private_dblist_path)
+ + parse_db_file(deleted_dblist_path))
+ private_tables = parse_db_file(private_tables_path)
+ filtered_tables = parse_db_file(filtered_tables_path, 'csv')
+
+ public_wiki_dbs = [db for db in all_dblist if db not in private_dblist]
+
+ public_dbs = list(public_wiki_dbs)
+ public_dbs.append('centralauth')
+ public_dbs.append('heartbeat')
+ public_view_dbs = [db + '_p' for db in public_dbs]
+
+ # these 2 are public dbs, not wiki views
+ public_dbs.append('information_schema_p')
+ public_dbs.append('meta_p')
+ return (private_dblist, private_tables, filtered_tables, public_wiki_dbs,
+ public_view_dbs, public_dbs)
+
+
+def get_private_databases(db, public_dbs, public_view_dbs, system_dbs,
+ user_databases_regex):
+ """
+ check no private databases are present
+ """
+ cursor = db.cursor()
+ databases = []
+ cursor.execute('SELECT schema_name FROM information_schema.schemata')
+ result = cursor.fetchall()
+ for row in result:
+ databases.append(row[0])
+ cursor.close()
+
+ non_public_wikis_present = ([d for d in databases if d not in public_dbs
+ and d not in public_view_dbs
+ and d not in system_dbs
+ and not re.match(user_databases_regex, d)])
+ return non_public_wikis_present
+
+
+def get_private_tables(db, private_tables, system_dbs, user_databases_regex):
+ """
+ check no private tables are present
+ """
+ cursor = db.cursor()
+ format_in_private_tables = ','.join(['%s'] * len(private_tables))
+ format_in_system_dbs = ','.join(['%s'] * len(system_dbs))
+ query = ("SELECT table_schema, table_name"
+ " FROM information_schema.tables"
+ " WHERE table_name IN ({})"
+ " AND table_schema NOT RLIKE %s"
+ " AND table_schema NOT IN ({})"
+ ).format(format_in_private_tables, format_in_system_dbs)
+ cursor.execute(query,
+ private_tables + [user_databases_regex] + system_dbs)
+ result = cursor.fetchall()
+ cursor.close()
+
+ return result
+
+
+def column_has_private_data(conn, database, table, column):
+ """
+ check that given column is not null or the empty string
+ """
+ cursor = conn.cursor()
+ has_private_data = False
+ try:
+ query = ("SELECT count(*)"
+ " FROM `{}`.`{}`"
+ " WHERE NOT (`{}` IS NOT NULL"
+ " OR `{}` <> '')").format(database, table,
+ column, column)
+ cursor.execute(query)
+ result = cursor.fetchall()
+ if int(result[0][0]) > 0:
+ has_private_data = True
+ # Ignore "table doesn't exist" errors
+ except pymysql.err.ProgrammingError:
+ pass
+ # Ignore "field doesn't exist" errors
+ except pymysql.err.InternalError:
+ pass
+ cursor.close()
+ return has_private_data
+
+
+def get_unfiltered_columns(conn, filtered_tables, public_wiki_dbs):
+ """
+ check no private fields are present
+ """
+ unfiltered_columns = []
+ for line in filtered_tables:
+ if line[2] == 'F':
+ table = line[0]
+ column = line[1]
+ for database in public_wiki_dbs:
+ if column_has_private_data(conn, database, table, column):
+ unfiltered_columns.append([database, table, column])
+ return unfiltered_columns
+
+
+def main():
+ (private_dblist, private_tables, filtered_tables, public_wiki_dbs,
+ public_view_dbs, public_dbs) = get_lists(all_dblist_path,
+ private_dblist_path,
+ deleted_dblist_path,
+ filtered_tables_path)
+ db = pymysql.connect(host='localhost', user='root',
+ unix_socket='/tmp/mysql.sock')
+
+ print('Non-public databases that are present:')
+ private_databases_present = get_private_databases(db, public_dbs,
+ public_view_dbs,
+ SYSTEM_DBS,
+ USER_DATABASES_REGEX)
+ print(private_databases_present)
+
+ print('Non-public tables that are present:')
+ private_tables_present = get_private_tables(db, private_tables, SYSTEM_DBS,
+ USER_DATABASES_REGEX)
+ print(private_tables_present)
+ print('Unfiltered columns that are present:')
+ unfiltered_columns_present = get_unfiltered_columns(db, filtered_tables,
+ public_wiki_dbs)
+ print(unfiltered_columns_present)
+
+
+if __name__ == "__main__":
+ main()
--
To view, visit https://gerrit.wikimedia.org/r/323525
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I66e43a1bc7554fd095ddda378a40856da115fdf9
Gerrit-PatchSet: 16
Gerrit-Project: operations/puppet
Gerrit-Branch: production
Gerrit-Owner: Jcrespo <[email protected]>
Gerrit-Reviewer: ArielGlenn <[email protected]>
Gerrit-Reviewer: Jcrespo <[email protected]>
Gerrit-Reviewer: Volans <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits