Wednesday, April 06, 2005 8:49 AM Jan Tielens

Fooling SPS: Download files without having appropriate rights

Last week I did some SharePoint consultancy for a Belgian University (LUC), where they had an interesting issue with SharePoint Portal Server areas. Each research group in the university has it's own WSS site(s) in which they work on documents. Once such a document is finished, the document is published to an SPS area. Ok nothing special you may think, but the tricky part is that only selected people have read access to the WSS site where the actual document is located. Remember that when you publish a document to a SPS area, only a link will appear in the area pointing to the corresponding site. So if a user which doesn't have read rights on the WSS site visits the area, he will see a link to the document. But if he clicks that link, he will get the famous login popup because he cannot access the document! So how do you solve this issue without having to replicate data?

 

I came up with following solution: impersonating another user to get the document from the WSS site, and passing the document back to the user who requested it in the first place. Of course the process should be transparent for the users. So far the theory, let's take a look at the technical implementation. Because I really like creating web parts, I've put everything inside one web part, built using the SmartPart of course. The web part's first job is to provide an alternative display to display the items that are published to an area. The intresting part here is to figure out which area the web part is located on:

// Get current area 
SPWeb currentWeb = SPControl.GetContextWeb(this.Context);
Area currentArea = AreaManager.GetArea(PortalContext.Current, currentWeb.ID);

Once you've got a hold of the area instance, you can use the Listings property to loop over all the published items:

string html = ""; 
foreach(AreaListing al in currentArea.Listings)
{
if(al.Status == ListingStatus.Approved)
{
html += string.Format("<a href='{0}'>{1}</a><br>" , this.Request.Url + "?url=" + al.URL ,al.Title);
}
}
Literal1.Text = html;

The code above displays all the items in pretty ugly, but simple, way (a string containing html put in a literal control). I leave it up to your imagination to figure out a nicer way. Notice that the destination of the link is the current page's url, with an url property added to the query string.

The second part of the web part is to do the impersonation magic to get the file which s url is specified in the query string. I've used a WebClient object to accomplish this, you can use it to easily impersonate a specific account and get the binary data of the document:

System.Net.WebClient wc = new System.Net.WebClient(); 
wc.Credentials = new System.Net.NetworkCredential("administrator", "secret");
Byte[] bytes = wc.DownloadData(Request.QueryString["url"]);

Once you get the binary data, you can send it to the Response object using the BinaryWrite method. Before that, you need to add some headers to the Response so the web browser will notice that binary data is sent. The code below will copy all the headers from the WebClient to the Response. At the end the Content-Disposition header is added, so the document will open outside the browser. If you omit this last header, the document will be shown inside the active browser instance.

foreach(string headerKey in wc.ResponseHeaders.Keys) 
Response.AppendHeader(headerKey, wc.ResponseHeaders[headerKey]);
Response.AppendHeader("Content-Disposition", "attachment;filename=" + fileName);
Response.BinaryWrite(bytes);

If you put both parts together you get following code in the PageLoad event of the Web User Control (remember that I'm using the SmartPart). If you're interested in the complete solution let me know, if there's enough interest, I'll clean up the code some more and publish this web part.

private void Page_Load(object sender, System.EventArgs e) 
{
if(Request.QueryString["url"] == "" || Request.QueryString["url"] == null)
{
// Get current area
SPWeb currentWeb = SPControl.GetContextWeb(this.Context);
Area currentArea = AreaManager.GetArea(PortalContext.Current, currentWeb.ID);
string html = "";
foreach(AreaListing al in currentArea.Listings)
{
if(al.Status == ListingStatus.Approved)
{
html += string.Format("<a href='{0}'>{1}</a><br>" , this.Request.Url + "?url=" + al.URL ,al.Title);
}
}
Literal1.Text = html;
}
else
{
Response.Clear();
System.Net.WebClient wc = new System.Net.WebClient();
wc.Credentials = new System.Net.NetworkCredential("administrator", "secret");
Byte[] bytes = wc.DownloadData(Request.QueryString["url"]);
string[] urlParts = Request.QueryString["url"].Split(char.Parse("/"));
string fileName = urlParts[urlParts.Length - 1];
foreach(string headerKey in wc.ResponseHeaders.Keys)
Response.AppendHeader(headerKey, wc.ResponseHeaders[headerKey]);
Response.AppendHeader("Content-Disposition", "attachment;filename=" + fileName);
Response.BinaryWrite(bytes); Response.End();
}
}
Filed under:

Comments

# re: Fooling SPS: Download files without having appropriate rights

Wednesday, April 06, 2005 7:09 AM by Rags Iyer

This can be achieved using Web Service too by giving network credentials,so can you please let me know how this approach can help to retrive document.

# re: Fooling SPS: Download files without having appropriate rights

Wednesday, April 06, 2005 2:42 PM by Steven Collier [MVP]

Interesting approach, but what happens when a user tries to seach for data ? SharePoint Portals search will respect the security of the documents and so not return these documents.

The approach I've taken to this problem is to move the documents to a new location before creating the listings. This also then allows minor versions to occur on the original file and not effect the published version.

# re: Fooling SPS: Download files without having appropriate rights

Wednesday, April 06, 2005 5:06 PM by Jeff Cate

Steven makes an excellent point. However, even his solution of moving the documents to a new location (presumably with security set so that the people that need to get to it, can) and then publishing them to the portal from there is a workaround at best.

The concept of being able to "publish" a final document from a WSS site to a portal Area should be integral to SharePoint. From a functional or business standpoint it makes sense that this would be a common activity.

Unfortunately (and in IMHO), this feature seems to be poorly conceived in SharePoint 2003. The idea of having a menu item (Submit to Portal Area) on the context menu in a Document Library to do the publishing is fine. The idea of only creating a Listing in the portal area when the user clicks on that menu item doesn't make much sense to me because of the security issue described by Jan and Steven as well as other reasons.

To me the act of "publishing" a document to a portal area should reflect that the document has been completed in the collaboration environment (WSS) and now is ready to be published to the enterprise-wide "reader" environment (SPS). The "final version" SHOULD reside in SPS. The original "working copy" could be deleted from the WSS site or, at the very least, marked in some way to indicate that it has now been published to an SPS area.

Seems to me that something like the following should happen when the user clicks on "Submit to Portal Area":

1. The user is able to choose an Area
2. The user is able to choose a Document Library in that Area
3. The document is copied to the selected Doc Lib
4. The user has the option to create a Listing in the Area (as well as any other Area(s)) that points to the document in the Area Doc Lib that is to be its home
4. The user has the choice of either deleting the original "working" document in the WSS site or marking it somehow as having been published to SPS.

All of this should happen when the user clicks on "Submit to Portal Area".

Of course, if I thought about it some more I might even want another feature or two, but at a minimum it would be nice to have something like this if anyone from the MS SPS team is reading.

# re: Fooling SPS: Download files without having appropriate rights

Wednesday, April 06, 2005 10:55 PM by Mark Kruger

Sorry to get in late but I completely agree with Steven and Jeff. The idea of impersonation is a creative one but the description Jeff gives is exactly how it SHOULD work. I'm catching up on my blogs and did see this post: http://randomelements.me.uk/blog/Lists/Blog/DispForm.aspx?ID=886

I havent had a chance to check the document mover tool out but it's definitely along the lines of what needs to be done. This tool appears to be more for admins to run since it is not web-based. That's a big drawback.

# re: Fooling SPS: Download files without having appropriate rights

Thursday, April 07, 2005 4:00 AM by Jan Tielens

Steven, Jeff, you are totally right! The solution I've used fits the needs of the customer. But I agree that the functionality Jeff described should be a part of SPS itself.

Mark, thanks for the link, I'll check it out.

# re: Fooling SPS: Download files without having appropriate rights

Thursday, April 07, 2005 3:16 PM by Steven Collier [MVP]

While i agree withthe sentiment that published content should move to the portal, I'm actually glad it doesn't given other 'features' of the product set.

WSS sites are so much easier to manage in terms of backup, scale up etc than a portal that I much prefer the content to remain within a team site.

We use a workflow tool (Nintex) to move documents to the 'public' team site, this means it all happens as part of the approval process. Once there the portal area owners get notified of the new content with an alert.

HTML content is the stumbling block here, in a web page document library the actual content is an attribute of the webpart, and is lost when the file is moved.

# re: Fooling SPS: Download files without having appropriate rights

Thursday, April 07, 2005 7:55 PM by Rui Quintino

Hi Jan,

That's an interesting technique, but won't it raise some security issues? We are basically giving anyone a chance to get *any* url with a more privileged account.

# re: Fooling SPS: Download files without having appropriate rights

Friday, April 08, 2005 2:40 AM by Jan Tielens

Hi Rui, yes if that account has access to them. Alternativly I would build some security checks based on your own rules before passing the file back.

Maybe I should have mentioned that in the post...

# re: Fooling SPS: Download files without having appropriate rights

Friday, April 22, 2005 3:12 PM by IDEAList

Hi Jan,
I was trying to add your RSS feed into Fire Fox and it gave me an error saying it was an invalid feed. If you click on your XML button it also throws an error.

It seems as if id doesn't like on of the characters in this article. Here is an excerpt of the text containing the character: " get the file which s url is specified " . The character between the word "which" and the "s" seems to be the problem.

I just thought I would point it out so you could fix it. :)

# re: Fooling SPS: Download files without having appropriate rights

Wednesday, April 27, 2005 12:19 PM by sdfsdf

dfsdfsdfsdf

# re: Fooling SPS: Download files without having appropriate rights

Tuesday, September 12, 2006 2:32 PM by Tim

Hi Jan,

How do you do this from inside a SmartPart?  I am just looking for a way to get the current Area from inside a SmartPart.  It is difficult to try things and debug when I cannot see any error messages inside the SmartPart (come to thinkof it, I will try a try..catch block and print the output in a literal).  Anyway, I think it is probably blowing up on PortalContext.Current.

Thanks,

Tim