Das Management der Autorisierung von Usern spielt für IT-Systeme eine zentrale Bedeutung. Denn nur so ist sichergestellt, dass die Nutzer einer Plattform genau den Zugriff auf die Daten erhalten, der sinnvoll erscheint. Dabei sollte die Berechtigungsvergabe aus fachlicher Sicht sinnvoll und aus rechtlicher Sicht legal sein. Eine Herausforderung für Betreiber von analytischen Plattformen mit einer vielfältigen Palette an Tools, Datenbanken und damit verbundenen Systemen ist, die Berechtigungsvergabe für die User an einer Stelle zentral und effektiv zu gestalten, um Transparenz und betriebliche Verwaltung zu ermöglichen.
In diesem Beitrag liegt der Fokus auf den technischen Möglichkeiten, mit denen man dieses Vorhaben umsetzen kann. Der Fokus liegt hierbei auf Applikationen und Datenbanken, die mittels SQL über eine zentrale Datenbank zum Speichern der Metadaten verwaltet werden können. Ein kleiner Exkurs in die Umsetzung für Applikation mit einer REST API als Schnittstelle wird hier auch besprochen. Zunächst wird darauf eingegangen, wie ein solches Konstrukt an Metadaten auf einer relationalen Datenbank aufgebaut werden kann.
Aufbau der Metadaten zur Berechtigungsvergabe
Viele populäre Systeme und Datenbanken nutzen für die Autorisierung ihrer User das Prinzip von RBAC (Role Based Access Control), so zum Beispiel auch Snowflake. Dem User gleichgestellt sind in RBAC-Datenbanken Gruppen und Rollen. Entsprechend der im zu verwaltenden System genutzten Entitäten werden dafür Tabellen in den Metadaten angelegt. Eine solche Tabelle, bspw. für die verfügbaren Rollen kann so aussehen:
ROLE_NAME | INSERT_TS | INSERT_USER | ACTIVE_FLAG |
ops_admins | 2023-11-12 14:10:08.000 | user1 | 1 |
ops_developers | 2023-11-22 16:08:01.129 | user2 | 1 |
user_fachbereich1 | 2023-12-02 09:05:07.385 | user2 | 1 |
user_fachbereich2 | 2023-12-03 10:10:09.499 | user2 | 0 |
Die Tabelle beinhaltet den Namen der Rolle (ROLE_NAME), den Zeitpunkt (Timestamp), zu dem dieser Eintrag in die Tabelle getätigt wurde (INSERT_TS) und eine Markierung, ob der Eintrag gültig ist (ACTIVE_FLAG). Nicht mehr gültige Einträge werden bei Verbindung mit den übrigen Metadaten nicht weiter berücksichtigt, bspw. über Filter in SQL-Statements.
Verbindung der Metadaten
Im nächsten Schritt erfolgt eine Verknüpfung der Metadaten für die Entitäten Rolle, Schema und Tabelle. Die Tabellen der einzelnen Entitäten sollten dabei im Optimalfall Primary Keys besitzen. Im Fall von Tabelle 1 besteht der Primary Key nur aus der ROLE_NAME. Die Verknüpfung der Entitäten erfolgt dann in gesonderten Tabellen. Nachfolgend ein Beispiel für die Beziehung Rolle zu Datenbankschema:
ROLE_NAME | DATABASE_NAME | SCHEMA_NAME | PRIVILEGE_TYPE | INSERT_TS | INSERT_USER | LAST_UPDATE_TS | LAST_UPDATE_USER | ACTIVE_FLAG |
user_fachbereich1 | database1 | schema1 | USAGE | 2023-12-03 14:15:18.111 | user1 | 2024-01-15 08:10:11.859 | user1 | 1 |
user_fachbereich1 | database1 | schema2 | USAGE | 2023-12-03 14:15:18.111 | user1 | 2024-01-15 08:10:11.861 | user2 | 1 |
user_fachbereich2 | database2 | schema1 | CREATE | 2023-12-03 14:15:18.111 | user2 | 2024-01-15 08:10:11.870 | user2 | 1 |
Auch bei Tabelle 2 sollte es einen Primary Key geben, in diesem Fall auf die Spalten ROLE_NAME, DATABASE_NAME und SCHEMA_NAME. Die Darstellung in Tabelle 1 ist dabei für eine Umgebung nutzbar, die sich in verschiedene Datenbanken und darin befindliche Schemata aufteilen lässt. Dies ist bspw. bei Snowflake der Fall. Sollte es nur eine Datenbank auf der Umgebung geben, so kann die Spalte DATABASE_NAME weggelassen werden.
Dieses Prinzip lässt sich auf alle für das jeweilige System notwendigen Entitäten übertragen. Dieses Konstrukt kann dabei auf einer relationalen Datenbank der Wahl implementiert werden, je nach verfügbarer Infrastruktur des Betreibers auf OnPremise oder einer etablierten Cloud-Umgebung.
Verknüpfung mit den Metadaten der Applikationen
Üblicherweise können SQL-Befehle auf einer relationalen Datenbank nur ausgeführt werden, wenn sich darin neben dem Vermeiden von Syntax-Fehlern auch keine inhaltlichen Widersprüche mit den Metadaten der Datenbank finden lassen. Mit den Metadaten der Applikationen sind hier z.B. Information Schemas auf der Snowflake gemeint.
Bei dem bisher beschriebenen Konstrukt an Metadaten zur Berechtigungsvergabe kann es jedoch z.B. auch vorkommen, dass Berechtigungen für Tabellen umgesetzt werden, die noch nicht angelegt worden sind. Um dies zu vermeiden, sollte eine Verknüpfung der Metadaten der Datenbank mit den konstruierten Metadaten in Betracht gezogen werden.
Eine Möglichkeit bietet sich hierbei durch Views. Hier ein Beispiel, wie man eine solche View aufbauen kann:
CREATE OR REPLACE VIEW compare_permissions AS
WITH tobegranted_permissions AS (
/*
Abgleich zu vergebende Berechtigungen mit Liste an Tabellen, für die das Vergeben möglich ist (da diese existieren)
*/
SELECT permissions.*
FROM permissions -- Tabelle mit Berechtigungen, die noch zu vergeben sind
JOIN available_tables -- Tabelle aus den Metadaten der Datenbank mit den verfügbaren Tabellen, auf die berechtigt werden kann
ON (available_tables.database_name = permissions.database_name)
AND (available_tables.schema_name = permissions.schema_name)
AND (available_tables.table_name = permissions.table_name)
),
existing_permissions AS (
/*
Abgleich bereits vergebene Berechtigungen mit Liste an Tabellen, für die das Entfernen der Berechtigungen möglich ist
*/
SELECT active_permissions.*
FROM active_permissions -- Tabelle mit den bereits eingerichteten Berechtigungen
JOIN available_tables -- siehe oben
ON (available_tables.database_name = active_permissions.database_name)
AND (available_tables.schema_name = active_permissions.schema_name)
AND (available_tables.table_name = active_permissions.table_name)
)
comparing_view AS (
/*
Hier werden die beiden obigen Tabellen abgeglichen, es entsteht eine Tabelle mit NULL-Werten für entweder die Spalten mit *tobegranted oder *existing
*/
SELECT
tobegranted_permissions.database_name AS database_name_tobegranted,
tobegranted_permissions.schema_name AS schema_name_tobegranted,
tobegranted_permissions.table_name AS table_name_tobegranted,
tobegranted_permissions.role_name AS role_name_tobegranted,
existing_permissions.database_name AS database_name_existing,
existing_permissions.schema_name AS schema_name_existing,
existing_permissions.table_name AS table_name_existing,
existing_permissions.role_name AS role_name_existing
FROM tobegranted_permissions
FULL OUTER JOIN existing_permissions
ON (tobegranted_permissions.database_name = existing_permissions.database_name)
AND (tobegranted_permissions.schema_name = existing_permissions.schema_name)
AND (tobegranted_permissions.table_name = existing_permissions.table_name)
AND (tobegranted_permissions.role_name = existing_permissions.role_name)
)
/*
Im letzten Schritt werden die Datensätze aus dem Abgleich entfernt, wo Soll und Ist übereinstimmen
*/
SELECT *
FROM comparing_view
WHERE (role_name_tobegranted IS NULL OR role_name_existing IS NULL);
Aus dieser View lassen sich dann SQL-Befehle ableiten, mit denen sich auf einer relationen Datenbank Berechtigungen vergeben und wieder entziehen lassen. Die Befehle lassen sich im Stil der folgenden Beispiele generieren. Diese können sich dann in einer Tabelle speichern und historisieren lassen.
SELECT 'GRANT SELECT ON ' || compare_permissions.schema_name_tobegranted || '.' || compare_permissions.table_name_tobegranted || ' TO ROLE ' || compare_permissions.role_name_tobegranted || ';' FROM compare_permissions;
SELECT 'REVOKE SELECT ON ' || compare_permissions.schema_name_existing || '.' || compare_permissions.table_name_existing || ' FROM ROLE ' || compare_permissions.role_name_existing || ';' FROM compare_permissions;
Diese Statements lassen sich auch für das Management der Zugehörigkeit von Usern zu Gruppen und Rollen nutzen. Eine Tabelle, aus der sich die Statements ableiten lassen, kann so aussehen:
ROLE_NAME | USER_NAME | INSERT_TS | INSERT_USER | LAST_UPDATE_TS | LAST_UPDATE_USER | ACTIVE_FLAG |
user_fachbereich1 | user1 | 2024-01-15 10:10:11.457 | user1 | 2024-01-15 11:23:58.591 | user1 | 1 |
user_fachbereich1 | user2 | 2024-01-15 10:10:11.458 | user1 | 2024-01-15 11:23:58.599 | user2 | 0 |
Die SQL-Befehle zur Generierung von REVOKE- und GRANT-Statements lauten dann
SELECT 'REVOKE ROLE ' || ROLE_NAME || ' FROM USER ' || USER_NAME || ';' FROM ROLE_USER WHERE ACTIVE_FLAG = 0;
SELECT 'GRANT ROLE' || ROLE_NAME || ' TO USER ' || USER_NAME || ';' FROM ROLE_USER WHERE ACTIVE_FLAG = 1;
Betrieb der Metadaten
Das Kernstück bei der Verwaltung des aufgebauten Metadaten-Konstrukt sind, wie bei einer relationalen Datenbank üblich, SQL-Befehle. Hier können verschiedene Ansätze gewählt werden: So können die Befehle auf der Datenbank direkt abgesetzt werden. Snowflake bietet dabei bspw. die Möglichkeit, Tasks zu verwenden, um die Commands zu schedulen. Diese Option ist optimal, wenn es nur eine einzige zugrundeliegende Datenbanktechnologie in der zu verwaltenden Umgebung gibt.
Gibt es mehr als eine Datenbank oder Applikation auf der zu verwaltenden Umgebung, so kann der Einsatz eines auf der Plattform bereits etablierten Tools sinnvoll sein. Zur Datenübertragung eignen sich Tools wie Talend oder dbt. Voraussetzung hierbei ist, dass das Tool Connectoren für jede zu verwaltende Datenbank und Applikation anbietet. Dies ist auf einer bereits etablierten Plattform zumeist gegeben. Bei einer noch aufzusetzenden Umgebung sollte dies berücksichtigt werden. Alternativ kann die Berechtigungsvergabe auch über eigens dafür geschriebene Skripte umgesetzt werden. Diese sollten in einer von der Plattform unterstützten Sprache geschrieben werden.
Berechtigungsvergabe über eine REST API
Auf einer analytischen Plattform kann es auch Applikationen geben, die ihre Autorisierung nicht über SQL-Befehle steuern lassen. Ein Fall sind Webapplikationen, die eine REST API für solche Zwecke verwenden. Ein Beispiel wäre hier die Anwendung Apache Ranger, die das Framework für das Management von Regeln (Policies) für den Datenzugriff auf Hadoop-Plattformen bereitstellt.
Apache Ranger verwendet eine REST API als Schnittstelle, um eine automatisierte Verwaltung der sogenannten Policies zu ermöglichen. Ein entsprechendes JSON, das als Input für Apache Ranger dient, für die Tabelle TABLE_NAME in Database DATABASE_NAME für die Gruppe GROUP_NAME in Apache Hive (Data Warehouse System für Hadoop-Plattformen) kann dann so aussehen:
template_policy = {'isEnabled': True, 'service' : 'Hive', 'name': '', 'policytype': 0, 'isAuditEnabled': True, 'resources': {}, 'policyItems': [], 'zoneName': ''}
# Security Zone in Apache Ranger
template_policy['zoneName'] = securityZone
template_policy['name'] = str(DATABASE_NAME) + '|' + str(TABLE_NAME)
template_policy['resources'] = {
{'database': {'values': [DATABASE_NAME], 'isExcludes': False, 'isRecursive': False},
'table': {'values': [TABLE_NAME], 'isExcludes': False, 'isRecursive': False},
'column': {'values': ['*'], 'isExcludes': False, 'isRecursive': False}
}
template_policy['policyItems'] = [{
'accesses': [{'type': 'select', 'isAllowed': True}, {'type': 'read', 'isAllowed': True}],
'users': [],
'groups': [GROUP_NAME],
'conditions' : [],
'delegateAdmins': False
}]
In diesem Fall wird die Berechtigung auf die Gruppe GROUP_NAME vergeben. Die Werte DATABASE_NAME, TABLE_NAME und GROUP_NAME werden aus einer Tabelle aus den Metadaten entnommen, die gemäß dem oben beschriebenen Konstrukt diese Entitäten miteinander verbindet.
Fazit
Das organisierte Management der Berechtigungsvergabe auf einer analytischen Plattform stellt Entwickler und Administratoren der Plattform vor eine Reihe von Herausforderungen. In diesem Beitrag wurde ein Konstrukt an Metadaten präsentiert, mit dem sich eine zentrale Datenbank für die Metadaten rund um die Autorisierung von Usern, Gruppen und Rollen für verschiedene Applikationen der Plattform umsetzen lassen.
Das vorgestellte Konstrukt und dessen Umsetzung konzentriert sich dabei auf Applikationen mit einem verfügbaren SQL-Kontext. Mit diesem können GRANT- und REVOKE-Statements eingesetzt werden. Es bietet aber durch Einsatz von dafür gebauten Skripten und ETL-Jobs auch die Möglichkeit, das Konstrukt auch für Applikationen ohne SQL-Kontext umzusetzen. Ein Beispiel haben wir hier mit Apache Ranger gegeben.
Perspektivisch lassen sich die hier vorgestellten Ideen und Ansätze flexibel erweitern. So kann man diesen auch auf andere Applikationen mit Schnittstellen abseits von JDBC Connectoren und REST API übertragen. Auch können neben den hier verwendeten Entitäten Rolle, Gruppe und User auch noch weitere Entitäten zur Berechtigungsvergabe eingeführt und gemanagt werden.