Friday 6 November 2009

UR 7 breaks Attachment Download code, and how to fix it

* 2009-11-13. I've updated this post to reflect some proposed SDK changes published by Microsoft to the MSDN Forums here. I'll probably update this post again for reasons I'll come to later, but I think it's worth making an initial update now *

It was going to happen at some point; a CRM hotfix rollup that broke existing supported customisation code. Update Rollup 7 adds some security changes that apply to the Attachment/download.aspx page. As a result the example code documented here in the SDK no longer works.

If you try the code, the user gets the error message "Access Denied, Invalid Operation". If you enable tracing, you see a message like the following:
"Message: INVALID_WRPC_TOKEN: Validate WRPC Token: WRPCTokenState=Invalid, TOKEN_EXPIRY=4320, IGNORE_TOKEN=False, TOKEN_KEY=b1Nd0byfEd6+gwAZuez7cauYiRnfHuMLAquFY1Ks22dHgxZiW5IrxSobkv9aVfbC, ErrorCode: -2140991221"

The fundamental problem is that CRM now expects additional parameters on the query string that contain a Token, and there is no way for us to generate that token. There's a significant question about why this change was made, but I'll leave that for another time.

The recommended workaround is the use the web services directly as described at http://social.microsoft.com/Forums/en-US/crmdevelopment/thread/2f46a3c4-1123-49dd-804e-2787ef367986, which I expect to make it into the SDK soon. This is close to a like-for-like replacement for the original SDK code in that it is the type of code you might write for a console application.

However, a popular use of download.aspx was to provide any easy client-side link to download an attachment, rather than doing it programmatically with .Net code. To get equivalent behaviour you'll need a custom .aspx page that gets the uses similar code to that above, but writes it the response stream using Response.BinaryWrite. I'm going to have to write this code soon, and when I do I'll post it on the MSDN Code Gallery, and link to it here; in the meantime if anyone has to write it themselves the other thing you should do is pass information on the Response-Header to specify the filename. From memory this involves something like:

Response.AddHeader("content-disposition","attachment; filename=" + outputFile);

11 comments:

kgorczewski said...

I was waiting for someone who will resolve attachment problem in UR7.

kgorczewski
My Dynamics blog http://bovoweb.blogspot.com

Stuart said...

Didn't this post previously mention a registry change that overcame this? If so what's happened to it...

David Jennaway said...

Stuart

Yes it did. The short(ish) answer is that MS aren't that happy on that information appearing on a blog that's sindicated on their sites.

There's an ongoing discussion about the security implications of the registry change, and while that's going on I thought it was polite to remove it.

In the meantime, I'm sure it won't be hard to find a cahced copy of the post on one of the search engines, or those sites that syndicate this blog

David

Goktug Atac said...

I replace my older codes with new codes, The major change is, that I use the web service to handle the attachment download but now I replace it and use FileStream.



System.Text.StringBuilder FileName = new System.Text.StringBuilder(System.Environment.GetEnvironmentVariable("TEMP")).Append(@"\" + DateTime.Now.ToString("yyyyMMddhhmmssffff") + "_" + ((activitymimeattachment)ent).filename);

//System.Net.WebClient myWebClient = new System.Net.WebClient();
//myWebClient.Credentials = System.Net.CredentialCache.DefaultCredentials;
//myWebClient.DownloadFile(url, FileName.ToString());

using (FileStream fileStream = new FileStream(Convert.ToString(FileName),FileMode.OpenOrCreate))
{
byte[] fileContent = Convert.FromBase64String(((activitymimeattachment)ent).body);
fileStream.Write(fileContent, 0, fileContent.Length);

}

Goktug - crmAkademi
www.crmAkademi.net

Arthur said...

David,
Could you help me with the registry change?
I'm a nerd on the other workaround

Unknown said...

Registry fix: https://community.dynamics.com/blogs/crmdavidjennaway/comments/41406.aspx

Dave Berry said...

I believe this is because the attachment.aspx method doesn't preserve the rights and privileges model of CRM when pertaining to authorized access to said attachments, and the new token model is a mechanism that achieves it. Creating and downloading attachments would be completely open for anyone to achieve. And the "token" is an internal part of CRM's authentication mechanisms, meaning our use of it is limited to the standard CRM API.
I can see why they have changed this, and am thankful they have done so.

David Jennaway said...

I'm afraid I have to disagree with you, Dave. Accessing an attachment via download.aspx is still subject to the standard CRM authentication and authorisation model - i.e. you need a valid CRM user account that has rights to access the attachment.

The only security that tokens add is a defence agains cross-site reqquest forgery (XSRF) attacks. XSRF attacks are only really an issue if you either have Cross-site scripting (XSS) problems, or if you have a web application that allows data modification via GET (rather than POST) requests. Given that neither of those issues apply to MSCRM, then I fail to find a good reason for requiring tokens to download an attachment in this way.

Dave Berry said...

Dave, I appreciate your analysis of the situation, but wonder if perhaps an IFD environment adds considerations to the matter. At any rate, I felt it worthwhile to post here an example of how to use inline-data URIs to work around limitations of interfacing with download.aspx:

http://crmentropy.blogspot.com/2010/10/tito-zs-iframe-embedded-image-code.html

Rizwan Ahmed said...

I am facing same issue.
here is the solution

http://cloudytech.blogspot.com/2013/06/access-denied-opening-attachment-in-crm.html

Rizwan Ahmed said...
This comment has been removed by the author.