Im heutigen Blogbeitrag möchten wir uns einem Detail des oft behandelten Themas der OLAP-Berechtigungen widmen, der Berechtigung von Kennzahlen.
Wir alle haben uns sicherlich schon mit der Frage beschäftigt, was sieht ein Benutzer tatsächlich, nachdem man sich bei der Rechtevergabe auf der OLAP-Datenbank ausgetobt hat. Dazu stellen wir einen kleinen, aber feinen Trick vor.
Spricht man mit IT-Administratoren, weisen wir immer auf einen grundlegenden Unterschied zwischen Berechtigungen von Microsoft Analysis Services (kurz: SSAS) und z. B. dem Dateisystem einer Microsoft Windows Systemumgebung hin: die Berechtigungen auf OLAP-Datenbanken sind additiv, nicht restriktiv!
Von Dateien oder Verzeichnissen ist man gewöhnt, dass wenn einmal einem Benutzer ein beliebiges Recht entzogen wurde, dann bleibt diese Restriktion erhalten, egal worüber sonst einem vielleicht ein höheres Recht eingeräumt wird. Dieser Grundgedanke bietet einen recht guten Schutz gegen Fehlkonfiguration und damit unerwünschte Zugriffsrechte.
Bei Microsoft Analysis Services Datenbanken gilt dieser Grundsatz leider nicht, weder für „normale“ Dimensionselemente noch für die Berechtigung von Kennzahlen. Einmal erlaubt = immer erlaubt; das ist die Devise!
Was es bei dem Einsatz von Berechtigungen auf Kennzahlen gesondert zu beachten gilt, schauen wir uns in der Demonstrationsdatenbank „Chair“ an.
Ausgangssituation
Die zwei Fachbereiche Vertrieb und Einkauf unseres fiktiven Stuhlherstellers sollen auf einem zentralen OLAP-Würfel mit einer gemeinsamen DeltaMaster-Applikation arbeiten, dürfen aber nur spezifische Kennzahlen sehen und analysieren.
In diesem Beispiel darf der Vertrieb nur die Kennzahlen ausgehend vom Umsatz bis zum Nettoumsatz analysieren, der Einkauf hingegen unterliegt keinen Einschränkungen. An dieser Stelle soll nicht auf die allgemeine Konfiguration von Berechtigungsrollen eingegangen werden, dies wird sehr anschaulich in den beiden Blogs zur Rechteverwaltung „2009-09-04 BOK Rechteverwaltung in SSAS Teil 1“ und „2010-06-04 BOK Rechteverwaltung in SSAS Teil 2“ behandelt.
Konfiguration
Legen wir zunächst zwei Rollen für die gewünschten Fachbereiche auf der OLAP-Datenbank über das SQL Server Management Studio (kurz: SSMS) an. Zusätzlich werden wir für den Test auch zwei lokale Benutzerkonten benötigen.
Für die Konfiguration von Berechtigungen der Kennzahlen wechselt man in das Register „Dimensionsdaten“ und wählt als Dimension die Kennzahlen aus. Man erkennt hier bereits einen ersten Unterschied, der Eintrag existiert nur auf Cube-Ebene. An dieser Stelle bitte einmal genau hinsehen; die Kennzahlen werden technisch in einer Dimension abgelegt, auch wenn sie im DeltaMaster nur über den Analysewertbrowser sichtbar sind.
Wie in der Ausgangsituation beschrieben, konfigurieren wir jetzt die beiden Berechtigungsrollen (der Verkauf darf nur die Kennzahlen Umsatz, Rabatt, Skonti und Nettoumsatz sehen).
Machen wir noch einen kleinen Funktionstest. An dieser Stelle kommt der eingangs erwähnte Trick zum Einsatz. Dazu öffnen wir einfach DeltaMaster mit einer zusätzlichen Verbindungseigenschaft namens „Roles“. Mit diesem Schalter sind wir auch als administrativer Benutzer in der Lage, das Berechtigungsergebnis einer Rolle zu simulieren, ohne uns extra mit einem Benutzer, der diesem Benutzerkonto zugeordnet ist, authentifizieren zu müssen.
Eine Übersicht der möglichen Verbindungseigenschaften findet man im Microsoft Developer Network http://msdn.microsoft.com/de-de/library/dn140245.aspx.
Wir erwarten nun bei Anzeige unseres Startberichts, dass nur noch die berechtigten Kennzahlen Werte aufweisen, richtig? Falsch, wir bekommen einen Fehler, also doch noch nicht fertig.
Womit hängt das zusammen? Werfen wir einen Blick in das Cube-Skript des Würfels, dort werden eventuell Teilbereiche des Würfels (in MDX: Scopes()) mit speziellen Rechenanweisungen belegt.
Calculate;
CREATE MEMBER CURRENTCUBE.[MEASURES].[Nettoumsatz]
AS [Measures].[Umsatz] - [Measures].[Skonti] - [Measures].[Rabatt],
VISIBLE = 1 ;
Scope([Measures].[Stückpreis]);
this = [Measures].[Umsatz]/[Measures].[Absatz];
end scope;
Scope([MEASURES].[DB]);
this = [Measures].[Umsatz] - [Measures].[Skonti] - [Measures].[Lohn] - [Measures].[Material] - [Measures].[Rabatt];
end scope;
Es wird bei der Initialisierung einer Sitzung mit dem OLAP-Würfel also bereits das Cube-Skript auf Gültigkeit geprüft. Da jetzt aber in den Scope-Anweisungen auf Kennzahlen referenziert wird, die für den aktuellen Benutzer nicht berechtigt sind, wird die etwas kryptische Fehlermeldung ausgegeben.
Um nun diesen Umstand zu verhindern, können wir uns der nützlichen Funktion IsError() bedienen. Diese gibt für die Prüfung eines MDX-Ausdrucks im Fehlerfall „true“ zurück und kann somit sehr gut in Kombination mit einer Iif()-Anweisung kombiniert werden. Erweitern wir unser Skript:
Calculate;
CREATE MEMBER CURRENTCUBE.[MEASURES].[Nettoumsatz]
AS [Measures].[Umsatz] - [Measures].[Skonti] - [Measures].[Rabatt],
VISIBLE = 1 ;
Scope(
IIF(
IsError([Measures].[Stückpreis]) = true
, NULL
, [Measures].[Stückpreis]
)
);
this = [Measures].[Umsatz]/[Measures].[Absatz];
end scope;
Scope(
IIF(
IsError([MEASURES].[DB]) = true
, NULL
, [MEASURES].[DB]
)
);
this = [Measures].[Umsatz] - [Measures].[Skonti] - [Measures].[Lohn] - [Measures].[Material] - [Measures].[Rabatt];
end scope;
Hat man diese kleine Erweiterung implementiert, startet eine bestehende Sitzung für die Benutzergruppe mit den eingeschränkten Rechten ohne Fehler und zeigt nur noch die berechtigten Kennzahlen an.
Halt, werden jetzt die MDX-Experten unter uns sagen. Ein Iif() und IsError() im Scope bedeutet ja, dass bei jedem Aufruf zur Laufzeit dieser Ausdruck ausgewertet wird und sich somit negativ auf die Abfragegeschwindigkeit auswirkt. Das sollten wir verhindern und bedienen uns einer statischen benannten Menge (engl. NamedSet) in Kombination mit einer Hilfskennzahl, da dieser Ausdruck nur einmalig beim Start ausgeführt wird. Hier die dafür notwendigen Erweiterungen:
Calculate;
CREATE MEMBER CURRENTCUBE.[MEASURES].[Nettoumsatz]
AS [Measures].[Umsatz] - [Measures].[Skonti] - [Measures].[Rabatt],
Calculate;
/*
Werden Kennzahlen in SCOPE-Befehlen verwendet, deren Verwendung zuvor über OLAP-Berechtigungen eingeschränkt wurde,
dann muss unbedingt eine isError()-Prüfung erfolgen
*/
CREATE MEMBER CURRENTCUBE.[MEASURES].[Nettoumsatz]
AS [Measures].[Umsatz] - [Measures].[Skonti] - [Measures].[Rabatt],
VISIBLE = 1 ;
/*
Anlegen neuer, leerer Kennzahl zur Berechtigungsprüfung
*/
CREATE MEMBER CURRENTCUBE.[Measures].[Stückpreis geprüft] AS
NULL
,VISIBLE = 0 ;
/*
Anlegen eines versteckten NamedSets zur Berechtigungsprüfung
Die Ausführung erfolgt nur bei der Initialisierung des Würfels
*/
CREATE HIDDEN SET [Stückpreis geprüft] AS
IIF(
IsError([Measures].[Stückpreis]) //wenn Benutzer kein Recht hat, Fehler erzeugen
,{} //und ein leeres Set generieren
,{[Measures].[Stückpreis geprüft]} //sonst wird die Hilfskennzahl verwendet
);
/*
Durchreichen der geprüften Hilfskennzahl
*/
Scope([Stückpreis geprüft]);
this = [Measures].[Stückpreis];
end scope;
…
Für weitergehende Berechnungen sollten folglich für alle Kennzahlen entsprechende Hilfskennzahlen und statische benannte Mengen erzeugt werden. Hier muss aber dazu gesagt werden, dass selten auf einigermaßen großen Datenmodellen keine Geschwindigkeitseinbußen gegenüber der direkten Verwendung von IsError() innerhalb der Scope-Anweisung auffallen. Es gilt also abzuwägen, ob der größere Implementierungsaufwand wirklich durch einen Performancegewinn gerechtfertigt wird.
Fazit
Natürlich ist über das Thema Berechtigungen schon des Öfteren geschrieben worden, z. B. in dem Beitrag zu Zellkommentaren („2014-11-21 KRE Berechtigungen für Zellkommentare“) oder den Spezialitäten mit Linked Measuregroups („2011-02-25 GST Berechtigungskonzept mithilfe von Linked MG“) sowie natürlich den beiden bereits erwähnten „Standardwerken“ (zumindest sollten Sie das sein) Rechteverwaltung in SSAS Teil 1 und 2.
Aber da es aus meiner Erfahrung in der Praxis immer wieder die unterschiedlichsten Situationen oder Varianten gibt, kann das hier beschriebene Detail dem einen oder anderen Leser vielleicht helfen, ein wenig Zeit und Nerven bei der Suche nach einer Lösung zu sparen.