Aktualizacja 2010-03-18: zaktualizowałem kod przykładu gdyż jak głupi zapomniałem, że w SharePoint może istnieć więcej witryn niż jedna a mój kod powodował, że te inne nie działały :)

Wydaje mi się, że wspomniałem już o takim rozwiązaniu u siebie na blogu, jednakże nigdy się nie zagłębiłem w to jak można to rozwiązać. Pomysł chodził za mną już od początku 2008 roku, nawet zrobiłem wstępną implementację i ona działała. Jednakże dopiero dzisiaj dzięki Przemkowi przysiadłem do tego.

Ale o co chodzi?

WSS/MOSS nie udostępniają mechanizmu zarządzania uprawnieniami do widoków na listach, przez co czasami tworzy się naprawdę pokrętne rozwiązania – własne GridView, Web Part z określonym widokiem czy nawet Query Web Part. Zawsze jednak rozwiązanie to nie jest takie dobre jak byśmy chcieliby ono było.

Istnieje inna opcja?

Jasne, że tak! Swojego czasu musiałem przeciążyć jeden przycisk w SharePoint by jego obsłużyć jego akcję tak jak klient chciał. Była to zabawa na całego gdyż nie okazało się to takie proste jak wyglądało a wszystko przez powalony typ ankiety w SharePoint.

Wtedy także dotarło do mnie, że nie tylko tą kontrolę mogę przeciążyć i w ten sposób zacząłem kombinować co można jeszcze z tym SharePointem zrobić :) Jednym z pomysłów jaki był to właśnie dodanie zarządzania uprawnieniami, ale podobnie jak House, jak już odkryłem tajemnice to mnie to po prostu przestało bawić.

Jak można do tej opcji dojść?

Szczerze mówiąc, ja chyba miałem szczęście. Ogólnie wiedziałem co chcę zrobić, otworzyłem Reflecotra i zacząłem przeszukiwać bibliotekę SharePoint pod słowami pod którymi myślałem, że kontrolka może się czaić. Pierwszy strzał okazał się trafiony: przeszukałem informacje po słowie kluczowym View które ograniczyło mi listę do niezbędnych elementów. Tam natrafiłem na klasę ViewSelector. Wiedząc, że SharePoint korzysta z szablonów starałem się znaleźć wykorzystanie tej kontrolki w DefaultTemplates.ascx – sama kontrolka nie była wykorzystana, jako tako, ale nazwy szablonów zawierały tą część nazwy, która mnie interesowała. W ten sposób dotarłem do kontrolki o nazwie ViewSelectorMenu. Po szybkim zapoznaniu się z jej implementacją (reflector) stwierdziłem, że da się ją przeciążyć :)

Jak można taką kontrolkę oprogramować?

Nie jest to tak skomplikowane jakby się wydawało. To co trzeba zrobić to przeciążyć metodę AddMenuItems() – w niej zaś wywołać bazową implementację i następnie działać już na kontrolkach menu, które zostały przez klasę bazową utworzone. Cała implementacja polega na tym by przelecieć po wszystkich takich kontrolkach, pobrać adres URL na który przekierowują, dobrać się za pomocą jego do obiektu SPView i zebrać wszystkie te kontrolki które chcemy usunąć w jednym miejscu.

Przykładowy kod to realizujący:

protected override void AddMenuItems()
{
    base.AddMenuItems();

    IList<Control> controlsToRemove = new List<Control>();

    foreach (Control c in MenuTemplateControl.Controls)
    {
        if (c is MenuItemTemplate)
        {
            MenuItemTemplate menuItem = c as MenuItemTemplate;
            string url = menuItem.ClientOnClickNavigateUrl;

            // omitting EditView and etc.
            if (url.Contains("_layouts"))
                continue;

            int pos = url.IndexOf("?");

            if(pos != -1)
                url = url.Substring(0, pos);

            if (!DoseUserHavePermission(SPContext.Current.Web.GetViewFromUrl(url)))
            {
                controlsToRemove.Add(c);
            }
        }
    }
    foreach (Control control in controlsToRemove)
    {
        MenuTemplateControl.Controls.Remove(control);
    }
    
}

Teraz dzięki temu w metodzie DoseUserHavePermission mamy dostęp do SPView i możemy zadecydować co zrobić z danym widokiem (czy widok powinien być użytkownikowi wyświetlony czy też nie).

Mając kodowy kod to co możemy zrobić by go już przetestować to podpisać naszą dllkę, wrzucić ją do GAC oraz dodać do SafeControls w Web.Config. Ostatnim krokiem jest edycja szablonu. By można było naszą kontrolkę wykorzystać dodajemy rejestrację Tagu na górze kontrolki:

<%@Register TagPrefix="lineByLine" Assembly="LineByLine.ViewPermsManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=57af8fcf04e76604" namespace="LineByLine.ViewPermsManager"%>

I następnie podmieniamy wystąpienie tagu <SharePoint:ViewSelectorMenu> na nasz Tag. Robimy iisreset i sprawdzamy jak pięknie działa nasze rozwiązanie.

Jeżeli nie podoba się wam edycja pliku DefaultTemplates.ascx to można dany szablon przenieść do osobnego pliku ASCX (pamiętajcie o przeniesieniu/skopiowaniu Register dla kontrolek waszych i SharePointa). Taka zmiana wymaga także IISReset.

Czy istnieje już takie rozwiązanie na rynku?

Nie sądze, ponad rok temu jak podałem komuś takie rozwiązanie to był zdziwiony, że tak się da. DataPolis zaś wykorzystuje ViewSelectorMenu do ukrywania view, jeżeli osoba nie ma uprawnienia do kolumny. Dodatkowo rozwiązanie ich jest rozszerzone o Iterator jak i dodanie sprawdzania uprawnienia do WebService.

Przykładowe rozwiązanie

Nie pisał bym postu by tylko wam powiedzieć jak to można zrobić. Sprawdziłem to sam jeszcze dzisiaj i działa. Do postu załączam przykładowy projekt, który umożliwia wam zarządzanie uprawnieniami do widoku za pomocą prostej (bardzo prostej) listy zawierającej meta dane dla nazwy listy, nazwy widoku i nazwy grupy.

image

Solution składa się tak naprawdę z dwóch projektów, zmodyfikowanej wersji SPG dla SharePointa, w której zamiast mappera po stringach jest mapper po expression oraz zamiast po internal name mapowianie idzie po Id, oraz z głównego rozwiązania.

  • Entities – zawierają tak naprawdę jedynie obiekty służące mapowaniu elementów konfiguracyjnych z list na obiekt i z obiektu na listy;
  • Extensions – to są metody pomocnicze, które wykorzystuje w innych miejscach (na przykład aktualizacja elementu bez wywoływania event receiverów);
  • Repositories – służą, jako dostęp do danych na liście wraz z mapowaniem obiektu na element listy i na odwrót. Jedynie repozytorium dostępu do uprawnień jest oprogramowane, jedynie z jedną metodą;
  • Templates – zwiera przeciążony szablon dla ViewSelectorMenu;
  • ViewPermsInstaller – jest to feature receiver, który konfiguruje SPG, tworzy listę zarządzania uprawnieniami dostępu. Dodatkowo zawarte tam się elementy rozwiązania takie jak delegate control i grupa menu;
  • Fields – tutaj zawsze przechowuje mapowanie nazwy kolumny na jej GUID, dzięki czemu mogę zastosować łatwo expression w konfiguracji repozytorium. Ze względu na to, że nie korzystam z ContentType, ID pól są ustawiane z kodu;
  • ViewPermsManager – jest to klasa która jest odpowiedzialna za sprawdzanie uprawnień;
  • ViewPermsDelegateControl – jest to kontrolka, która sprawdza czy użytkownik jak wprowadził adres do View z palca to czy ma prawo widzieć to View, jeżeli nie to przekierowuje go na stronę z informacją o braku dostępu;
  • ViewPermsSelectorMenu – jest to klasa która zarządza elementami wyświetlanymi w selektorze widoków.

Ogólnie nic skomplikowanego. Całe rozwiązanie nadaje się do deployu (wykorzystuje SharePoint Tools for VS 1.3). To co otrzymacie po zainstalowaniu to:

1) Menu z linkiem do listy zarządzającej

image

2) Banalny formularz do zarządzania uprawnieniami

image

3) Ograniczone View

image

image

 

4) Przekierowanie na stronę z błędem, jeżeli użytkownik nie ma uprawnień do widoku a wklepał z palca URL

image

To czego rozwiązanie nie obejmuje

To jest prosta implementacja pomysłu. Rozwiązanie jest tak zorganizowane, żebyście mogli łatwo kod rozszerzyć o dodatkowe elementy/dodatkową walidację.

Łatwiej będzie odpowiedzieć na pytanie co jest :)

  • Ograniczenie elementów w wyborze View o te, które mają się pokazać danej grupie;
  • Uprawnienia dotyczą każdego – nie sprawdzania czy osoba jest administratorem ;)
  • Przekierowanie na stronę z błędem jeżeli osoba wklepała z palca URL do widoku.

Czyli za pomocą WebService wciąż będzie się można dostać do listy. Z poziomu kodu także. Jednakże nie powinno to mieć znaczenia skoro Target Audiences wystarczają ludziom to i to powinno wystarczyć :)

Na koniec

Z rozwiązaniem róbcie co chcecie :) modyfikujcie, edytujcie, dajcie tylko znać proszę czy wam się podoba i najważniejsze czy wam to działa. Oczywiście piwo też by było mile widziane :)

Jeżeli stworzycie coś fajnego na podstawie tego to też dajcie znać :) będzie na pewno miło :)

Link do rozwiązania.

11 KOMENTARZE

  1. Własnie szukam czegoś podobnego!!! W sumie to chodzi nawet o prostszą rzecz, a mianowicie czy da się tak ustawić prawa, aby dany użytkownik mógł otworzyć dokument z biblioteki, ale nie mógł widzeć całej listy?

  2. Mozesz to zrobic ustawiajac uprawnienia per element na liscie. osoby beda widziec jedynie dokumenty do ktorych maja uprawnienia. Reszta bedzie "ukryta" przed ich oczami.

  3. No niby tak, ale jest to strasznie upierdliwe, bo trzeba to robić per element…. .
    W sumie to ten system uprawnień jest troszke bez sensu. Wyobraźmy sobie, że mam listę tasków i chciałbym, aby pełny dostęp do nich miała tylko określona grupa osób oraz osoba, do której task jest przypisany. Podobnie jak z dokumentami – po przypisaniu tasku do osoby trzeba jeszcze sie naklikać, aby dodać tej osobie prawo do edicji bo SP (wss 3) sam nie wpadnie już na to, że jak komuś przypisuje się zadanie to musi mię prawo od edycji…. No może jeszcze czegoś niedoczytałem…. ;)

  4. mozesz zablokowac dany view dla osob z grupy Y i ustawic im jedynie Assign To [Me] wtedy beda widziec tylko swoje taski. zas grupie X daj pelne uprawnienia do widokow – obejscie ale skuteczne.

    Lub programowo ustawiaj uprawnienia (podawaj false w break inherence bo inaczej Ci sie lista zatka)

  5. rozwiązanie za pomocą views’ów zarzuciłem bo zawsze każdy sobie może zacząć przeglądać task po tasku wpisująć w adresie przeglądarki:
    http://…../EditForm.aspx?ID=numerek

    Programowo odpada – bo w tym projekciku nie programuje nic po stronie serwera ( w sensie kodowania w VB/C#). Nie robiłem tego a SP nie pozwala dodawać kodu na stronach aspx. Pewnie skończy się na wywoływaniu procedurki w webserwisie http://<Site>/_vti_bin/Permissions.asmx… i tak już na maksa używam webserwisów do robienia zestawień z tych tasków…

    Szkoda, że nie można tego zrobic z tego co Out of the box, w Workflow’ach też by sie kurcze przydało przypisuwanie uprawnień. A tu nic.

  6. nie doceniasz userów :) !!!!

    oni nie muszą odpowiadać na takie trudne pytania. Są jak małpy – klikają, klikają i w końcu wyklikają. A te nieszczęsne taski i dokumenty zawierają poufne informacje.
    I nawet nie wyzywając użytkowników od małp – nie chciałbym, aby w tak prymitywny spósóbosoby nieuprawnione miały dostęp do tego do czego dostępu mieć nie mogą.
    Cóż problem zostaje i daje nura w w webservis do praw. Używam już SPServices i ten http://spservices.codeplex.com/wikipage?title=Permissions&referringTitle=%24%28%29.SPServices wydaje się obiecujący.

  7. Witam,

    Rozwiązanie na nie działa – sprawdzałem. Ze względu na np. Ribbona, mechanizm wyboru widoków menu został zmieniony. Teraz oprócz zrobienia swojej wersji menuselectora, trzeba jeszcze zrobić delegatecontrol, który zrobi za nas podmianę selectora na widoku listy. Polecam sprawdzić stronkę vsmenu.aspx z layouts i skoncentrować się na delegate control z id ViewSelectorMenuDelegate.

Comments are closed.