Thursday, May 14, 2009

Enumerate Role Assignments to retrieve Groups and Users Permissions - Generating a complete report

Introduction:

The following SharePoint Application Page with C# in line code enumerates all the roles assignments of a Windows SharePoint Services 3.0 or MOSS site collection and displays for each web site:
  • The role member and reports if it is a Group or an User.
  • If it is a group, displays the number of users in the Group and the users list.
  • If it is a group, displays the permissions for all significant Site Collection elements (if inheritance was broken for a List, a Folder or an Item, this will be specified). It is information at a cross-site level.
  • In any case (User or group), displays the Roles list for the current web site.
  • In any case (User or group), displays for each previous roles, the Roles Permissions list.
This post and its code sample is an improvement of the previous post Enumerate Role Assignments to retrieve Groups and Users Permissions in a Windows SharePoint Services 3.0 or MOSS Site . I also did this new post to help a reader that has asked me information about SharePoint Roles and Permissions reporting.
Why to use it?

In Windows SharePoint Services 3.0, access to Web sites, lists, folders, and list items is controlled through a role-based membership system by which users are assigned to roles that authorize their access to Windows SharePoint Services objects.

To give a user access to an object, you can do so either by adding the user to a group that already has permissions on the object, or by creating a role assignment object, setting the user for the role assignment, optionally binding the role assignment to the appropriate role definition with base permissions, and then adding the assignment to the collection of role assignments for the list item, folder, list, or Web site. If you do not bind the role assignment to a role definition when assigning a user to a role, the user has no permission.

As there is two ways of granting permissions to a specific user, this can easily lead to a lack of organization regarding Security Granting Policy, and you may have some SharePoint sites to clean up.

When you want to check the users and the groups present in a Site Collection web sites and their role, it can take time doing it by browsing "People and Group" administration pages for each web site. It would be nice to display all the information in a single report. The following code sample will give you this kind of report, and it will be easier for you to reorder Users and Groups using it.

How to use it?

Copy the following code in an .aspx file.
Paste the file in the LAYOUTS directory.
"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS"

Browse the page with site administrator permissions from any site using the usual Application Page url (If you have named the aspx page enumerateroles.aspx access it via url ...myWebSite/_layouts/enumerateroles.aspx).
Check Report in the page.

Code of the Role Assignments Report Application Page:


<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"
    Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
 
<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" %>
 
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.SharePoint.Utilities" %>
<%@ Import Namespace="System.Security.Permissions" %>
<%@ Import Namespace="Microsoft.SharePoint.Security" %>
<%@ Import Namespace="System.Diagnostics" %>
<asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <asp:Label ID="lblOutPut" runat="server" />
    <asp:Panel ID="pnlHidden" runat="server" />
 
    <script type="text/javascript" language="JavaScript">
 
        document.getElementById('<%=pnlHidden.ClientID %>').style.display = 'none';
 
        function Toggle(node) {
            // Unfold the branch if it isn't visible
            if (node.nextSibling.style.display == 'none') {
                // Change the image (if there is an image)
                if (node.children.length > 0) {
                    if (node.children.item(0).tagName == "IMG") {
                        node.children.item(0).src = "/_layouts/IMAGES/collapseminus.gif";
                    }
                }
                node.nextSibling.style.display = '';
            }
            // Collapse the branch if it IS visible
            else {
                // Change the image (if there is an image)
                if (node.children.length > 0) {
                    if (node.children.item(0).tagName == "IMG") {
                        node.children.item(0).src = "/_layouts/IMAGES/collapseplus.gif";
                    }
                }
                node.nextSibling.style.display = 'none';
            }
        }
    </script>
</asp:Content>
 
<script runat="server">       
 
    string htmlOutput = "";
 
    protected string WriteIsRootWeb(SPWeb aWeb)
    {
        if (aWeb.IsRootWeb)
        {
            return " (This is the Site Collection Root Web)";
        }
        else
        {
            return "";
        }
    }
 
 
    [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        using (SPSite mySite = SPContext.Current.Site)
        {
            string siteUrl = mySite.Url;
            this.Page.ClientScript.RegisterClientScriptBlock(base.GetType(), "GroupPermissionCallback", "\r\nfunction WebForm_DoCallback(controlId,url,GroupPermissionCallback,ctx,unknownNullValue,unknownBooleanvalue)\r\n{\r\n    var strUrl ='" + siteUrl + "/'+ url;\r\n    open(strUrl, '_blank');\r\n}\r\n\r\n", true);
        }
    }
 
    public override void VerifyRenderingInServerForm(Control aControl){}
 
    protected override void Render(HtmlTextWriter writer)
    {
        bool isAgroup = true;
        SPGroup aGroup = null;
 
        foreach (SPWeb aWeb in SPContext.Current.Site.AllWebs)
        {
            htmlOutput += "\n<br>******************************************";
            htmlOutput += "\n<br><span style='color:blue'>Roles Assignments Report on web site " + aWeb.Title + WriteIsRootWeb(aWeb) + "</span>";
            htmlOutput += "\n<br>******************************************<br>";
 
            htmlOutput += "\n<br><div style='padding-left:40px'>List of " + aWeb.Title + " Groups";
 
            foreach (SPGroup Group in aWeb.Groups)
            {
                htmlOutput += "\n<br>" + Group.Name + " ID: " + Group.ID;
            }
 
            htmlOutput += "\n</div>";
            htmlOutput += "\n<div style='padding-left:20px'>";
            foreach (SPRoleAssignment aRole in aWeb.RoleAssignments)
            {
                isAgroup = true;
                htmlOutput += "\n<br>*************<br>";
                try
                {
                    aGroup = aWeb.Groups.GetByID(aRole.Member.ID);
                }
                catch
                {
                    isAgroup = false;
                }
 
                if (isAgroup)
                {
                    htmlOutput += "\n<br><span style='color:#357EC7'>Group Id : " + aRole.Member.ID.ToString() + " | " + " Principal Name : " + aRole.Member.Name + "</span>";
 
                    int numberOfusers = aWeb.Groups.GetByID(aRole.Member.ID).Users.Count;
                    htmlOutput += "\n<br><br>Number of users:" + numberOfusers;
                    aGroup = aWeb.Groups.GetByID(aRole.Member.ID);
                    htmlOutput += "\n<br>";
                    if (numberOfusers > 0)
                    {
                        htmlOutput += "\n<br>List of " + aGroup.Name + " users";
 
                        foreach (SPUser aUser in aGroup.Users)
                        {
                            htmlOutput += "\n<br> - " + aUser.Name;
                        }
                    }
 
                    GroupPermissions myGroupPerm = new GroupPermissions();
                    myGroupPerm.GroupId = aRole.Member.ID;
 
                    System.IO.StringWriter myStrWriter = new System.IO.StringWriter();
                    HtmlTextWriter myWriter = new HtmlTextWriter(myStrWriter);
 
                    pnlHidden.Controls.Add(myGroupPerm);
 
                    myGroupPerm.GroupId = aRole.Member.ID;
                    myGroupPerm.RenderControl(myWriter);
 
                    htmlOutput += "<br><br><a onClick='Toggle(this)'><IMG style='text-decoration:none;border:0px' SRC='/_layouts/IMAGES/collapseplus.gif' /><span style='cursor:hand'>All (cross-sites) Permissions for " + aRole.Member.Name + "</span></a><div style='width:98%;display:none;'>" + myStrWriter.ToString() + "</div>";
                    htmlOutput += "\n";
                }
                else
                {
                    htmlOutput += "\n<br><span style='color:#3BB9FF'>User Id : " + aRole.Member.ID.ToString() + " | " + " Principal Name : " + aRole.Member.Name + "</span>";
                }
                htmlOutput += "\n<br><br>role(s) for " + aRole.Member.Name + " in " + aWeb.Title + ": <br>";
                foreach (SPRoleDefinition aRoleDefBinding in aRole.RoleDefinitionBindings)
                {
                    htmlOutput += "\n<br> - " + aRoleDefBinding.Name + "   (" + aRoleDefBinding.Description + ")";
                    htmlOutput += "\n<div style='padding-left:10px;'>List of permissions for " + aRoleDefBinding.Name + ":";
                    htmlOutput += "\n<br>" + aRoleDefBinding.BasePermissions.ToString();
                    //htmlOutput += "\n\n" + aRole.RoleDefinitionBindings.Xml + "\n\n";//to see the xml from View Source of the page
                    htmlOutput += "\n</div>";
                }
 
                htmlOutput += "\n<br>";
            }
            htmlOutput += "\n</div><br>";
        }
 
        htmlOutput += "\n<br>*************<br>";
 
        lblOutPut.Text = htmlOutput;
 
        base.Render(writer);
    }
</script>
 



Monday, April 20, 2009

Populate an ASP .Net Treeview from a SQL self-join table

I had last week an ASP .net TreeView control to fill from a self-join table. I looked on the Internet to find an Algorithm but I did not find any solution using C# or VB .Net but a solution in Code Project using SQL or other using DataSets. I then decided to post the following Algorithm that might help somebody else.
Here is the table I used in the example and the generated treeview image on the right.


Location.ID Location.Name Location.ParentID
1 Americas 0
2 Europe 0
3 Asia 0
4 Northern America 1
5 Western Europe 2
6 Western Asia 3
7 Eastern Asia 3
8 South-Eastern Asia 3
9 Northern Europe 2
10 Southern Europe 2
11 Canada 4
12 United States 4
13 Netherlands 9
14 France 9
15 Israel 5
16 Japan 6
17 Spain 10
18 United Kingdom 9
19 Vietnam 8
20 Quebec 11
21 California 12
22 District of Columbia 12
23 England 18
24 Hefa 15
25 Midi Pyrenees 14
26 Nara 16
27 New Jersey 12
28 Noord-Brabant 13
29 Ontario 11
30 Santa Cruz De Tenerife 17

 

Here is the code I used to generate the treeview:

<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Page Language="C#" %>

 

<%@ Import Namespace="System.Data.SqlClient" %>

<%@ Import Namespace="Microsoft.SharePoint" %>

<%@ Import Namespace="System.Collections.Generic" %>

<form runat="server">

<asp:treeview runat="server" id="TreeView1" />

</form>

 

<script runat="server">

   

    public class Location

    {

        private string _locationName;

        private int _locationId;

        private int _parentLocationId;

 

        public string LocationName

        {

            get { return _locationName; }

            set { _locationName = value; }

        }

 

        public int LocationId

        {

            get { return _locationId; }

            set { _locationId = value; }

        }

 

        public int ParentLocationId

        {

            get { return _parentLocationId; }

            set { _parentLocationId = value; }

        }

    }

        //list of all nodes

        SortedList<int, Location> myLocations = new SortedList<int, Location>();

        //list of all created nodes

        SortedList<int, TreeNode> myCreatedNodes = new SortedList<int, TreeNode>();

 

    public void Page_Load(object sender, EventArgs e)

    {

        string result="<br>start loading  page : " + DateTime.Now.Second + " " + DateTime.Now.Millisecond;

        try

        {

            System.Data.SqlClient.SqlConnection myCon = new System.Data.SqlClient.SqlConnection("Server=localhost;Database=myCompany;integrated security=SSPI");

 

            myCon.Open();

            SqlCommand myCommand = new SqlCommand();

 

            myCommand.CommandText = @"select ID,

                                        Name,

                                        ParentID

                                        from Location

                                        ";         

           

            myCommand.Connection = myCon;

            SqlDataReader myReader = myCommand.ExecuteReader();

            Location myLocation = null;

            while (myReader.Read())

            {

                myLocation = new Location();

                myLocation.LocationName = Convert.ToString(myReader[1]);

                myLocation.LocationId = Convert.ToInt32(myReader[0]);

                myLocation.ParentLocationId = Convert.ToInt32(myReader[2]);

                myLocations.Add(myLocation.LocationId, myLocation);

            }

 

            myCon.Close();

 

            TreeNode aNode = null;

            result+="<br>start processing treeview : " + DateTime.Now.Second + " " + DateTime.Now.Millisecond;

           

            foreach (int aKey in myLocations.Keys)

            {

                string code = myLocations[aKey].LocationId.ToString();

                aNode = new TreeNode(myLocations[aKey].LocationName, code);

                CreateNode(aNode);

            }

            result += "<br>end processing treeview : " + DateTime.Now.Second + " " + DateTime.Now.Millisecond;

        }

        catch (Exception ex)

        {

            Response.Write("error<BR>" + ex.Message);

            Response.Write("Trace<BR>" + ex.StackTrace);

        }

        Response.Write(result);

    }

 

    public void CreateNode(TreeNode aNode)

    {

        //This list stores all the nodes id from the current node to the ultimate parent

        List<int> myPath = new List<int>();

        if (!myCreatedNodes.ContainsValue(aNode))//if the node was not alreazdy created

        {

            int nodeId=1001;

            nodeId = Convert.ToInt32(aNode.Value);

            //Building the current node path untill the ultimate parent

            myPath.Add(nodeId);

            while (nodeId != 0)

            {

               if(nodeId!=0){

               nodeId= myLocations[nodeId].ParentLocationId;

               myPath.Add(nodeId);

                }

            }

        }

       

        //descending from Ultimate parent until the node

        //if the current node does not exists we create it and add it to created nodes collection.

        //if it has not a parent we add it to the treeview

        //if it has a parent,the parent was already created because we come from it, so we add the current node to the parent.

        TreeNode nodeToAdd = null, ParentNodeTofind = null;

        for (int j = myPath.Count - 1; j > -1; j--)

        {

            if (myPath[j] != 0)

            {

                //checking for each path if the nodes was already created

                if (!myCreatedNodes.Keys.Contains(myLocations[myPath[j]].LocationId))

                {

                    //creating the node and adding it to the created nodes collection.

                    nodeToAdd = new TreeNode(myLocations[myPath[j]].LocationName, myLocations[myPath[j]].LocationId.ToString());

                    myCreatedNodes.Add(myLocations[myPath[j]].LocationId, nodeToAdd);

 

                    int parentId = myLocations[myPath[j]].ParentLocationId;

                    //checking if the node has a parent

                    if (parentId == 0)//this node has no parent we add it to the tree view

                    {

                        TreeView1.Nodes.Add(nodeToAdd);

                    }

                    else//this node has a parent

                    {

                        //rerieving parent node (sure to find it)

                        ParentNodeTofind = myCreatedNodes[myLocations[myPath[j]].ParentLocationId];

                        //we add the node to its parent childNodes

                        ParentNodeTofind.ChildNodes.Add(nodeToAdd);

                    }

                }

            }

        }

    }

</script>