Berechtigungen im Reportserver

Leider bietet uns der Report-Manager keinen vernünftigen Überblick, über die vergebenen Berechtigungen. Wer einen solchen Überblick für eine Revision oder einen Umzug von Reports benötigt, hat es schwer.

Das folgende Datenmodell zeigt uns die fünf beteiligten Tabellen, die in diesem Artikel besprochen werden sollen.

Catalog

In diesem Beitrag geht es um verschiedene Tabellen, die alle zusammen die Beziehungen von Usern, Rollen und Objekten beschreiben. Anfangen sollte man mit dem Reportserver..Catalog, welchen ich schon vor ein paar Wochen beschrieben habe. Hierüber erhalten wir die Information über die Art und Bezeichnung der Objekte.

Users

Demgegenüber stehen die User, welche sich in der Tabelle Reportserver..Users befinden. Die UserID wird für die weiteren Verknüpfungen benötigt.

Roles

Die Tabelle mit den Rollen hatte ich erst vor kurzem beschrieben, daher hier nur der Hinweis darauf, dass uns in diesem Artikel nur der Name der Rolle interessiert. Für Details werden wir dann auf den Report mit den genauen Eigenschaften der Rolle verweisen.

Policies

Bei jedem Objekt wird eine Policy hinterlegt. In der Tabelle selber steht nicht viel mehr als eine ID. Interessant ist dann aber, was mit dieser ID gemacht wird, um User und Rollen ins Spiel zu bringen.

PolicyUserRole

In dieser Tabelle steht die Verknüpfung der User mit Rollen und den Policy-IDs. Das heißt, ein User kann an einem Objekt (vertreten durch die Policy-ID) über verschiedene Rollen berechtigt sein. Der Primärschlüssel dieser Tabelle ist ein synthetischer Schlüssel, der keine weitere Aussage hat.

Jetzt kann man sich fragen, warum denn die Policy-ID benötigt wird, wenn es doch auch direkt die Objekt-ID tun könnte. Der Grund liegt darin, dass die vererbten Berechtigungen immer dieselbe Policy-ID verwenden. Also haben alle Objekte mit ererbten Rechten, die Policy-ID des übergeordneten Verzeichnisses.

Informationen auswerten

Wir wollen zum einen darstellen, welche Rechte ein User für die verschiedenen Objekte (Verzeichnisse, Reports, Datenquellen, ...) hat, und zum anderen ob sich die Rechte der Verzeichnisse von den Rechten der in ihnen enthaltenen Objekte unterscheiden. Leider steht in der Tabelle Catalog kein Pfad der Objekte separat drin, sondern immer nur der Pfad mit dem Namen des Objektes. Zum Vergleich der Rechte von Verzeichnissen und anderen Objekten müssen wir also den Pfad aus den anderen Objekten extrahieren. Hier ist leider einige String-Akrobatik notwendig. Der Teil vor dem letzten / ist der komplette Pfad bis zu dem Bericht.
Wir verwenden also den Trick, dass wir den Pfad einmal umgekehrt anschauen (reverse) und dann den ersten Schrägstrich finden (charindex). Ab diesem Schrägstrich nehmen wir den Rest des Pfades (substring) und drehen ihn dann zur besseren Lesbarkeit wieder um (reverse).

SELECT Path, REVERSE(SUBSTRING(REVERSE(c.path),CHARINDEX('/',REVERSE(c.path))+1,LEN(c.path))) AS Verzeichnis
FROM Catalog c
WHERE TYPE <> 1;

Verzeichnisrechte und Objektrechte

Wir wollen nun einfach die Verzeichnisrechte und die Objektrechte nebeneinanderstellen. Der FULL JOIN findet anhand des Users, der Berechtigung am Objekt und dem Pfad (= Verzeichnis) des Objektes, welches mit dem Pfad des Verzeichnisses verglichen wird, statt. Für jedes Verzeichnis und jedes andere Objekt werden u. a. die Usernamen ausgegeben, damit man darüber z. B. filtern kann. Dies bringt uns aber noch einen weiteren Vorteil. Sollte bei diesem FULL JOIN ein User-Name auf Objekt-Ebene nicht gefüllt sein, auf Verzeichnis-Ebene aber schon, dann hat der User mehr Rechte auf das Verzeichnis, als auf das Objekt. Umgekehrt gilt auch, dass wenn ein User-Name auf Verzeichnis-Ebene leer ist, aber für die darin enthaltenen Objekte gefüllt ist, hat der User mehr Rechte auf die Objekte, als auf das Verzeichnis.

Diesen Umstand nutzen wir später im Report aus und geben über einen Ausdruck an dieser Stelle den Text "Abweichende Rechte" aus. Gleichzeitig setzen wir noch die Background-Color, damit es wirklich direkt ins Auge springt.

WITH Objekt_Permission
AS
(SELECT DISTINCT u.UserName, c.Name, c.path AS Objekt, r.RoleName, r.Description, REVERSE(SUBSTRING(REVERSE(c.path),CHARINDEX('/',REVERSE(c.path))+1,LEN(c.path))) AS Verzeichnis,
CASE
  
WHEN TYPE = 1 AND Path NOT LIKE '%Users Folder%' THEN 'Folder'
  
WHEN TYPE = 1 AND Path LIKE '%Users Folder%' THEN 'Users Folder'
  
WHEN TYPE = 2 THEN 'Report'
  
WHEN TYPE = 3 THEN 'Resource'
  
WHEN TYPE = 4 THEN 'Linked Report'
  
WHEN TYPE = 5 THEN 'Data Source'
  
WHEN TYPE = 6 THEN 'Report model'
  
WHEN TYPE = 8 THEN 'Shared Dataset'
  
WHEN TYPE = 9 THEN 'Report Part'
  
ELSE 'nicht bekannt'
END AS Type_Desc  
FROM dbo.Roles r
JOIN dbo.PolicyUserRole pur
    
ON r.roleid = pur.roleid
JOIN dbo.Users u
    
ON pur.userid = u.userid
JOIN dbo.Policies p
  
ON pur.PolicyID = p.PolicyID    
JOIN dbo.Catalog c
  
ON c.PolicyID = p.PolicyID        
WHERE UserName NOT IN ('BUILTIN\Administrators', 'Domain Users', 'IUSR_REPORT02')
),
Verzeichnis_Permission
AS
(SELECT DISTINCT u.UserName, c.Name, c.path, r.RoleName, r.Description
FROM dbo.Roles r
JOIN dbo.PolicyUserRole pur
    
ON r.roleid = pur.roleid
JOIN dbo.Users u
    
ON pur.userid = u.userid
JOIN dbo.Policies p
  
ON pur.PolicyID = p.PolicyID    
JOIN dbo.Catalog c
  
ON c.PolicyID = p.PolicyID        
WHERE UserName NOT IN ('BUILTIN\Administrators', 'Domain Users', 'IUSR_REPORT02')
AND
c.TYPE = 1                            -- Nur Verzeichnisse
)
SELECT COALESCE(o.Verzeichnis, V.Path) AS Verzeichnis, o.Name, o.Objekt, o.Type_Desc, o.UserName, o.RoleName, v.UserName AS UserName_Verzeichnis
FROM Objekt_Permission O
FULL JOIN Verzeichnis_Permission V
ON O.Verzeichnis = V.Path
AND O.UserName = V.UserName
AND O.RoleName = V.RoleName
ORDER BY 1, O.Objekt, O.UserName, O.RoleName;

Report

Jetzt brauchen wir nur noch einen Report, der uns diese Informationen anzeigt. Der Report erhält außerdem einen Parameter @User, damit wir die Daten später einmal filtern können. Damit können wir auch gezielt die Auswertung für einen einzelnen User machen, um dessen Rechte entweder als Vorlage für andere User zu verwenden, oder zu modifizieren, falls der Anwender das Aufgabengebiet wechselt.

Der Report verwendet in der Datenquelle übrigens einen Alias (Reporting), um ihn einfacher verteilen zu können. Zur Verwendung von Alias habe ich hier ja bereits einiges geschrieben.

Für die Beschreibung der Rollen wird der bereits oben erwähnte Bericht aus dem vorangegangenen Artikel aufgerufen.

Am besten schaut sich jeder selber einmal seine Daten auf der Reportserver-Datenbank an, da diese einfach nachzuvollziehen sind. Bei dem ein oder anderen wird es eine Überraschung geben, wenn man feststellt, welche Rechte einige User (immer noch) haben. Aber der Report-Manager unterstützt einen ja auch nicht gerade bei der Administration der Berechtigungen.
Weiterhin sollte man immer im Hinterkopf haben, dass diese Tabellen von Microsoft so nicht dokumentiert sind und sich ohne Vorwarnung ändern können. Man sollte also möglichst keine kritischen Anwendungen dagegen programmieren oder sogar versuchen Daten in diesen Tabellen zu manipulieren.

Beispiel

Im folgenden Bild sehen wir die Daten, die wir für einen User gefiltert haben und es zeigt uns unter dem Stamm drei Folder. Auf diese drei Folder hat der User also Rechte erhalten.

Punkt 1 und 2 zeigen uns einen Folder (S...) und die Reports darin. Für beide Bereiche hat der User dieselben Rechte, da hier die Rechte vererbt werden.

Punkt 3 und 4 zeigen uns zwei Folder, die im oberen Bereich nicht auftauchen, wo es aber Objekte drin gibt, für die der User Rechte hat. Die Abweichung wird rechts durch den roten Text dokumentiert.

Der User hat außerdem Rechte auf den Folder "Data Sources", aber auf kein Objekt, was sich darin befindet. Entweder ist der Folder leer, oder es wurden keine Rechte auf die Objekte darin vergeben, wobei diese dann auch nicht die Rechte vom Folder erben dürfen.

  SSRSPermissions.sql
  RS Berechtigungen.rdl
  RS Rollen Berechtigungen.rdl