# Safely return all lists in site collection that match criteria

I'm using the following code to get all "Announcement" lists in a Web Applications site collection.

Unfortunately, sometimes the current user does not have permission to that site and the page fails with an exception, even inside the try block.

What would be the right way to do the following safely for all users, where even an anonymous user would just get no results?

static public List<SPListMeta> AllSiteAnnouncementsLists()
{
var returnList = new List<SPListMeta>();
foreach (SPSite oSiteCollection in SPContext.Current.Web.Site.WebApplication.Sites)
{
var collWebs = oSiteCollection.AllWebs;
try
{
foreach (SPWeb oWebsite in collWebs)
{
using (oWebsite)
{
var collSiteLists = oWebsite.GetListsOfType(SPBaseType.GenericList);
returnList.AddRange(from SPList oList in collSiteLists where oList.Title == "Announcements" select new SPListMeta(oList));
}
}
}
catch
{
}

}
return returnList;
}


Exception is firing on foreach (SPWeb oWebsite in collWebs)

-

Retrieve data only
If you're only querying the items in these list then consider the following two alternatives to looping through everything:

If you can live with the delay caused by crawling use search to gather the items.

If not and you're in the "normal" situation where if a user has access to any sub site he also has at least some access to the site collection root, then you should change your code to only get the oSiteCollection.RootWeb inside a try/catch and then use SPSiteDataQuery to query the announcement list in all subsites.

Other access If your user always have access to the parent of site they have access to then you can access the root site and then as @SPArchaeologist mentinoned use GetSubwebsForCurrentUser recursively.

If access is "random" then you need to have some thing like this:

// Get login of user in some way

var siteIds = new List<Guid>();
SPSecurity.RunWithElevatedPrivileges(() =>
{
using (var elevatedSiteCollection = new SPSite(oSiteCollection.ID))
{
foreach (SPWeb elevatedSite in elevatedSiteCollection.AllWebs)
{
{
}
}
}
});
foreach (var id in SiteIds)
{
using (SPWeb oWebsite = oSiteCollection.OpenWeb(id))
{
...
}
}

-
Thanks! Very close here, I think. I don't want to get the list items, only the names of the list. Once a user selects a list I will query it directly. I'm having trouble, because (for example) if the webapplication is http://rootsite, then http://rootsite/my is part of the webapplication.sites, and the user may have no access there, and any attempt to read any data from the SPSite object will yield an exception. –  Wesley Oct 15 '12 at 7:05
See last part of updated answer –  Per Jakobsen Oct 15 '12 at 7:17

Check if user has permission to the web before you "use" the web, SPWeb.DoesUserHavePermissions is the method you want. Example -

if(oWebsite.DoesUserHavePermissions(SPBasePermissions.Open))
using (oWebsite)
{
//code
}


kindly let me know if it worked.

-
Thanks! I should have mentioned the exception is firing in the foreach (SPWeb oWebsite in collWebs) line. This would work great if I could enter into that loop and evaluate each web independently. –  Wesley Oct 15 '12 at 6:10

From your comment it would seem that your problem is on the line:

 var collWebs = oSiteCollection.AllWebs;


That call requires the user to have access to the web instances you are enumerating - it will fail as soon as you try to access a web site the user doesn't have access to. It is also useless to try to use workarounds like SPWeb.DoesUserHavePermission because you would get an access denied exception even before the call.

What you can do is the following: open a web site (maybee the root site?) that you are sure the user have access to, and then enumerate all the site under that site. You can do this with the following line of code.

SPWebCollection websForCurrentUser = theCurrentSite.OpenWeb().GetSubwebsForCurrentUser();


Here "theCurrentSite.OpenWeb()" is a call to the OpenWeb() method of a SPSite istance to get hold of the SPWeb you will base your search on - notice that you will need to have access to that "base" web site. After that you just call GetSubwebsForCurrentUser.

-
Read the last line. I'm failing ONE line later (so close). I'm having trouble, because (for example) if the webapplication is http://rootsite, then http://rootsite/my is part of the webapplication.sites, and the user may have no access there, and any attempt to read any data from the SPSite object will yield an exception. Strangely, it lets me get the AllWebs object, but any attempt to read from it will yield an exception. I need to determine if the user has access to the SPSite object, but not one on earth seems to know how to make SPSite.DoesUserHavePermissions() work –  Wesley Oct 15 '12 at 7:08
Yes, this is the second part of the problem. My solution would work if you have access to the root site of the site collection (rootsite/my in your example). If you don't even have access to the site root, or you are in any other situation where permission can be "random" you need an alternate approach as per Jakobsen answer. Notice that in that case, you may fall in the situation where an user has access to a leaf weband doesn't have acces to any underling node. Remember this when enumerating all the web if you go for that approach. –  SPArchaeologist Oct 15 '12 at 8:00

try using SPSecurity.. it will help..

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spsecurity.runwithelevatedprivileges.aspx

-
But this will return lists that the user doesn't have access to. I want to maintain security, just not at the expense of application stability. –  Wesley Oct 15 '12 at 4:42
then you have to check that current user has permission for that list or not.. –  Torque Oct 15 '12 at 5:24
Right, which gets us back to the question at hand. How does one do that without accessing the list iteself? All my attempts so far will generate an exception. –  Wesley Oct 15 '12 at 5:47