Tuesday, August 10, 2010

IFD, WindowsIdentity, CrmImpersonator and Double Hop Deployment


In my previous post, I mentioned how do we authenticate between external database and CRM databases. But, last Friday, I realized that this is not 100% correct for all deployment scenarios.

Last week, one of our clients requested us to deploy CRM application in different boxes, i.e. they want us to install AD, SQL Server and CRM application in THREE separate boxes. After installation and configuration, we found that CRM is working properly. But, we noticed that all our custom aspx pages cannot retrieve data from external database through stored procedures using EXECUTE AS command. There is no error in impersonation process. This happened because there is a double hop issue in our deployment.

For other projects, we can resolve this issue by configuring delegate trust for SQL server in Active Directory. Unluckily, we cannot use this approach in this project because there is a security restriction in this organization. So, I have to figure it out how to resolve this issue by programming way.

After searching through Google, I found a very helpful post describing about how to resolve double hop issue in CRM. Thanks to Joe Ladas for his valuable contribution. He did mentioned that we should use SET CONTEXT_INFO command in our stored procedure to retrieve data. I tested it by hard coding USERID in our stored procedure and it is working properly.

So, I changed our code to retrieve UserId from CRM using WhoAmI request. Then, another issue is coming out. I cannot retrieve correct user id from WhoAmI request. Every time I received was a SYSTEM USER account id. This happened because we are using CrmImpersonator class in our aspx pages. I have to used CrmImpersonator class because our deployment also need to support for Internet Facing Deployment (IFD).

From Joe Ladas's post, I noticed that he was using WindowsIdentity Impersonation to retrieve UserId through WhoAmI request for AD Authentication. I followed the same way. I used calledId from CrmAuthenticationToken for IFD (CrmPostAuthentication) and WhoAmI request for AD authentication. It is working perfectly.

Below is the code sample I used for testing application. Hope it can help you all in someway :) Either leave comment or email me if you want to know further information or give suggestion to me. Thanks!

Retrieving Current User Id for IFD & AD Authentication
public Guid GetCurrentUserId(HttpContext context, string orgName)
{
    Guid _userId = Guid.Empty;

    // For IFD
    if (context != null && context.User.Identity.AuthenticationType == "CrmPostAuthentication")
    {
        CrmAuthenticationToken token = CrmAuthenticationToken.ExtractCrmAuthenticationToken(context, orgName);
 _userId = token.CallerId; // Get Current User Id
    }

    // For AD
    if (_userId == null || _userId == Guid.Empty) _userId = WhoAmI().UserId;
    return _userId;
}

public WhoAmIResponse WhoAmI()
{
    System.Security.Principal.WindowsIdentity identity = (System.Security.Principal.WindowsIdentity)System.Threading.Thread.CurrentPrincipal.Identity;
    System.Security.Principal.WindowsImpersonationContext impersonationContext = identity.Impersonate();

    WhoAmIRequest request = new WhoAmIRequest();
    WhoAmIResponse response = null;

    if (this.useInterface)
        response = this.iservice.Execute(request) as WhoAmIResponse;
    else
        response = this.service.Execute(request) as WhoAmIResponse;

    if (response == null) throw new UnauthorizedAccessException("System cannot retrieve WhoAmIResponse.");
    impersonationContext.Undo();

    return response;
}

Retrieving Data from CRM through External Database
public DataTable GetAllList()
{
    SqlClientHelper oSqlHelper = new SqlClientHelper(connString);
    SqlParamCollection oSqlParam = new SqlParamCollection();
    oSqlParam.AddParam("@userID", _userId);
    DataTable dt = oSqlHelper.ExecuteQuery("sp_GET_AllContacts", CommandType.StoredProcedure, oSqlParam);
    oSqlHelper.Dispose();

    return dt;
}

Stored Procedure I used in External Database
CREATE PROCEDURE [dbo].[sp_GET_AllContacts] 
-- Add the parameters for the stored procedure here
    @userID uniqueidentifier
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
 
    -- Insert statements for procedure here
    DECLARE @original uniqueidentifier
    SET @original = CONTEXT_INFO()
    
    -- Setting Current User Identity
    SET CONTEXT_INFO  @userID

    -- I am using Synonym Name in my External Database
    SELECT ContactID, LastName FROM FilteredContact

    -- Restoring Original Identity  
    IF @original IS NULL
        SET CONTEXT_INFO 0x
    ELSE
        SET CONTEXT_INFO @original
    END

Reference:
Understanding Kerberos Double Hop
Querying a Filtered View in a Custom Application with a Double Hop
Authentication and Impersonation between CRM database and External databases
Authentication from a custom ASPX page and token AuthenticationType – AD/IFD
MSSQL Synonym Generator

Tuesday, July 20, 2010

XRM Field-Level Security Plugin


One of the most frequently asked features in xRM/CRM development is "Field Level Security" access rights control. According to Microsoft's White Paper, we can implement "Field Level Security" feature by combination of TWO mechanisms i.e; plug-in and SQL Query.

To be honest, I always tried to run away from it whenever my users request for this feature. I have my own reason. I am worry that we can have unforeseen issues in some areas such as business logic implementation, and access rights conflict, if we don't have well plan for field-level access rights controls.

Today, I cannot runaway anymore because one of my clients wants this feature to limit read access for some attributes which we kept for information purpose only. So, with the help of Andriy's contribution and White Paper, I implemented this feature. Of course, the approach will be different from them as usual :)


Below is the list of features supported by my plug-in:
  • Everyone will have ALL access in CRM as default, unless you specify attribute information detail in access rights entity
  • Field-level access rights is controlled by "User Role", and it will apply to all same User Roles across all Business Units.
  • It provides THREE types of access rights: 1) No Access, 2) Read Only, and 3) Read & Create
Feel free to use this application. Either leave comment or email me if you want to know further information or give suggestion to me. But, please use it as your own risk :)

Download Link


Reference:

Tuesday, June 22, 2010

Clone Record in CRM using CrmServiceToolkit (JavaScript)

A few weeks ago, one of our customers requested record cloning features in some of our customized entities. I tried to look for this feature through internet and found out a lot of interesting articles stating different ways of cloning records.

Below is the list of articles I found out for your reference:
  1. Clone Records Without Code
  2. Clone Records Using Workflow
  3. Cloning Records In Microsoft CRM 4.0 Using AJAX Controls
Among them, I decided to use ASP.NET way. I download the source code, modified it a bit and used in our project. After testing for a few days, I noticed about five or six seconds delay to load an asp.net page when I click "Clone Record" button for the first time.

I discussed about this issue with one my colleagues and he suggested me to use CRM Web Service Toolkit (JS) framework to clone the record. It is not very easy to use, but it helps us a lot to solve our performance issues and reduce steps in deployment. We are using this toolkit to retrieve data from CRM directly instead of writing our own asmx web services applications. Of course, you need to reference CrmServiceToolkit in your JavaScript deployment. Thanks to Daniel Cai for his research, great effort and generous contribution to our CRM community.

Below is my JavaScript source code:

 
onCloneRecordClick = function()
{
    try
    {
        if(confirm('Do you want to create a duplicate record?'))
        {
            // Create Clone Record and Retrieve Primary Key
            var _id = makeClone(crmForm.ObjectTypeName, crmForm.ObjectId);
           
            // Open Newly Created Record in CRM

            if(confirm('Duplicate record created successfully.\nDo you want to open it?'))
            {
                var _url = getFormURL(crmForm.ObjectTypeName, _id);
                window.open(_url, "_self");
            }
        }
    }
    catch(ex)
    {
        showError(ex.description);
    }
} 

// Create Clone Record
makeClone = function(entityName, objId)
{
    var attribName = crmForm.ObjectTypeName + "id";
    var fetchXML =     "" +
                    "" +
                    "";        
    var oSource = CrmServiceToolkit.Fetch(fetchXML);        
    
    // Create Clone Record
    var oClone = new CrmServiceToolkit.BusinessEntity(entityName);        
    for ( var p in oSource[0].attributes)
        if(p != attribName)
            oClone.attributes[p] = oSource[0].attributes[p].value;
        
    var id = CrmServiceToolkit.Create(oClone);
    return id;
}

// Retrieve URL to Open CRM Form
getFormURL = function(entityName, objId)
{
    var path = "../../../userdefined/edit.aspx?id=" + objId + "&etn=" + entityName;
    return path;
}

// Show Error Message 
showError = function(message)
{
    attachOnChangeAllControls();

    var notificationsArea = document.getElementById('Notifications');
    
    if (notificationsArea == null)
    {
        alert(message);
         return;
    }
    notificationsArea.title = '0';
    var notificationHTML = '









' + message + '
';     notificationsArea.innerHTML += notificationHTML;     notificationsArea.style.display = 'block'; } 

Explanation:
(added on 2nd Sept 2010)

How to use CrmServiceToolkit in CRM?
1. Download CRM Service Toolkit from codeplex
2. Extract zip file and place CrmServiceToolkit.min.js and qunit.js files under ISV folder
3. Use loadscript() function to load CrmServiceToolkit file to CRM at Form Load Event (Please refer to henrycordes' blog) This is not recommended by Microsoft.
4. Use above CloneRecord code in your CRM Form.


Reference:

Thursday, June 3, 2010

FilteredView and ASP.NET Impersonation

A few days ago, we re-deployed a CRM project in staging server for maintenance purpose. We did normal re-deployment processes such as backup live database, imported it into staging server, run deployment manger to re-configure the settings, re-deployed extension pages, etc.

We tested CRM application and it was working fine. Yesterday, we noticed all extension pages (aspx) using  "Filtered Views" to retrieve data from SQL Server were not working properly. We are using "Filtered Views" in stored procedures to retrieve data from SQL Server. 

Below is the list of steps I did for troubleshooting:
  1. The login user was a member of System Administrator Role. So, it could not be an Access Rights issue.
  2. I run one stored procedure under login user's credential (using EXECUTE AS command) and it was working properly. I can retrieve the data. So, impersonation process was working fine and it can be either configuration error or impersonation cannot go through from website to SQL Server.
  3. I checked all registry values, application settings, IIS configurations, IE settings and system configuration data in MSCRM_CONFIG and CRM database respectively. All settings were correct.
  4. Finally, I added <identity impersonate="true" /> in web.config file of our extension project. Then, system prompted there was another impersonation setting already configured in that file. I searched the value and found that impersonation was set to false.
This happened because one of my colleagues accidentally configured impersonation setting through IIS's website properties. This is not a big issue, but it took me about one hour to resolve this. So, I blog this post to save your time and save my time in future if I forgot it. :-)

Reference:

Saturday, April 24, 2010

Only one usage of each socket address (protocol/ network address/ port) is normally permitted


A few weeks ago, we faced above issue while we were conducting stress test operations on one of our xRM application modules. The program read data from various CRM entities (estimated about 7+) through MS CRM sdk and wrote 7,000 plus records into a text file based on predefined file format. We used File Helper 2.0 for our file generation.

One of our colleagues suggested to reduce client TCP/IP socket connection timeout value in CRM application server's registry and it worked fine. Below is the steps we have to follow:
  1. Browse to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
  2. Create/Update DWORD values MaxUserPort and enter a decimal value between 5000 and 65534
  3. Create/Update DWORD value TcpTimedWaitDelay and enter a decimal value between 30 and 300
This happened because when a client initiates a TCP/IP socket connection to a server, the client typically connects to a specific port on the server and requests that the server respond to the client over TCP or UDP port. Under certain conditions it is possible that the available ports in the default range will be exhausted. Please review below reference link for more information.

Reference:

0x80048428: The duplicate detection rule is currently being used and cannot be updated or deleted. Please try again later.

Last week, my colleague tried to unpublished a duplicate detection rule in CRM and received following error message: "The duplicate detection rule is currently being used and cannot  be updated or deleted. Please try again later."

After searching through google for a while, I found out a very useful post from MS CRM forum. It is stated that there may be a pending task in CRM system job which is associated to that duplicate detection rule and needed to be canceled. After following this instruction, I managed to unpublish that duplicate detection rule from MS CRM.

Reference:

Tuesday, March 30, 2010

Re-create your deleted AD user account which is using in CRM

Sometimes you can accidentally delete AD user account which is linked to CRM user from your domain. Even if you re-create same AD account at that moment, this AD account will not automatically link back to your CRM user. To solve this issue, please follow below instructions:
  1. Create one dummy user account in your AD
  2. Update domain name of CRM user with newly created dummy account (Please make sure First Name and  Last Name are populated automatically)
  3. Create AD user account that you want to tie back to your CRM account
  4. Update domain name of CRM user with that AD user account
Caution: Do NOT Disabled your CRM account while it was not linked to AD user account. If you do so, you cannot Enabled that CRM account and received an error. In this case, you have to read this article to manually update your ADUserGuid in CRM SystemUser entity. After that you can follow above steps.

Reference:

Sunday, February 21, 2010

Replicate Local Data Group (Offline Client / Outlook)


If you are using offline client application in CRM implementation, you will notice that configuring local data group is a very painful task for all developers. For me, most of our customers are using offline client applications in their daily operations. We have to configure local data group for all laptops based on their user roles and configuration.  It took more than one and half hour to finish the whole configuration setup for each laptop.

Last year, one of our customers suggested a console application called MSCRM: Automate Local Data Group Creation. It is very helpful application for us. We need to setup configuration for only one laptop (one user role) and copy it to the rest of  users' laptops. Amazingly, it took only a few minutes to finish the whole process. But, there still  has some draw backs. It required a few configuration steps to finished very carefully. Here is the list of steps for configuration:
  1. Go to SQL server and delete some records
  2. Create one text file which contains the list of views that you want to copy, and
  3. Run batch program with pre-defined parameters/commands, etc.
In short, it is not possible to ask users to help configuration on themselves.  They still need supervision of professionals. One mistake can screw up the whole database and operations.

So, I decided to do re-engineer works based on existing application. I changed existing web service calles to CRM 4.0, take out the first step (SQL delete process) and add user interface to automate the whole process. Thanks to David Jennaway for contributing his knowledge, idea and source code to community.

Below is the screen shot of my "Replicate Local Data Group" application:



You can run this application from any location/computer if you have access to their CRM application. 

Caution: Please backup your CRM user database first before running this application. Application will delete all Local Data Groups of destination users to prevent duplication.

System Requirement:
  • .NET Framework 2.0
 Access Matrix:
  • The login user who is connecting to CRM must be a member of PrivUserGroup. If you done more thant one installation for CRM, you will fine multiple PrivUserGroup in AD. Please make sure you are using correct one for login user.
  • All replicate users must have CREATE, READ, WRITE  and DELETE access to their own records (User) in "Saved View" entity. You can find "Saved View" entity under Core Record tab of your user role.
Download Link:

Reference:

Saturday, February 13, 2010

Authentication and Impersonation between CRM database and External databases

Sometime we may need to create stored procedures and views to access CRM data for faster retrieval and processing. To do so, we have to use an external database to store all stored procs and views since Microsoft does not recommend to keep our stored procs, functions and views in CRM database.

For me, I am using an external database for THREE obvious reasons:
  1. Processing very complex business logic through stored procs
  2. Recording data changes for future reference (for example; audit data)
  3. To retrieve data using impersonation (through plug-in and/or C# code)

Below is the steps you may need to configure if you want authentication and impersonation between CRM database and external databases.(In this example, I am using "crm admin" account as a service account to do authentication and impersonation.)

In SQL Server Management Studio:

1. SQL Server >> Security >> Login >>
    Create domain\crmadmin in account MS SQL Server

2. SQL Server >> Security >> Login >> Select User properties >> Server Role >>
    Give domain\crmadmin to sysadmin server role

3. CRM/Extenal Database >> Security >> Login >>
    Add domain\crmadmin account and domain\ReportingGroup {GUID} account into orgname_CRM database and external database

4. SQL Server >> Security >> Login >> Select User properties >> User Mapping >>
    Map domain\crmadmin account to THREE databases (MSCRM_CONFIG, orgname_CRM, External database) and give database role to db_owner and public

5. SQL Server >> Security >> Login >> Select User properties >> User Mapping >>
    Map domain\ReportingGroup {GUID} account to TWO databases (orgname_CRM, External database) and give database role to public
6. Extenal Database >> Database Properties >> Permissions >>
    Choose domain\ReportingGroup {GUID} accountand give explicit permission to GRANT CONNECT, EXECUTE and SELECT

7. Run grant impersonation of crmadmin to reporting group

USE [master]

grant impersonate on login :: [domain\crmadmin] to [domain\ReportingGroup {GUID}]

8. Use EXEC AS LOGIN = 'domain\crmadin' for crmadmin impersonation if necessary

9. Use REVERT after every impersonation

Tips for development:
  1. To make your live easier in writing and deploying SQL scripts, please use my MSSQL Synonym Generator to create synonym names for you.
  2. If your CRM plug-ins need to access data from SQL server through impersonation, please use stored procedure and defined you impersonation code there. (You cannot pass through EXEC AS LOGIN, Stored Proc. and REVERT commands together in sqlcommand.commandText)
  3. If your environment does not have enough CALs to access remote desktop to use SQL server, you can refer EXEC AS LOGIN command to access SQL data to through your laptop/PC.
Reference:

Friday, February 12, 2010

Impersonate CrmService web service calls by using CallerId

At times users access a resource as though they were someone else. This is known as impersonation. For example, in CRM, when we tried retrieved "saved views" from userquery entity, you can only retrieve those views that are belongs to you even though you are using CRM Admin account to retrieve those information.

Below is the code sample to impersonate CrmService calls in CRM 4.0:
CrmAuthenticationToken token = new CrmAuthenticationToken();
token.OrganizationName = _strOrganization;
token.AuthenticationType = 0;
token.CallerId = userid; // userid that you want to impersonate

CrmService crmService = new CrmService();
crmService.Url = _strServerUrl + "/MSCRMServices/2007/crmservice.asmx";
crmService.CrmAuthenticationTokenValue = token;

// You can use System.Net.CredentialCache.DefaultCredentials
crmService.Credentials = new NetworkCredential("username", "password", "domain");

Caution: The credential user must be a member of PrivUserGroup. (You can check it under Active Directory Users and Computer) If is not assigned properly, you will receive following error message:
error code: 0x80040204
description: Invalid user auth.
error type: Platform
Reference:

Wednesday, February 3, 2010

A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond


A few day ago, my colleague got above error message when he clicked on extension (aspx) pages in CRM application. Though he tried to browse extension pages from Development server, the IP address shown in error message was always point to LIVE server one. I checked database server setting in Deployment Manager and Windows Registry. All configuration settings were correct.

Below is the detail error message:
Exception information:
    Exception type: SocketException
    Exception message: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 192.168.1.21:5555  

Thread information:
    Thread ID: 7
    Thread account name: NT AUTHORITY\NETWORK SERVICE
    Is impersonating: False
    Stack trace:    at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
   at System.Net.Sockets.Socket.InternalConnect(EndPoint remoteEP)
   at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Int32 timeout, Exception& exception)

Resolution:
After searching through google for a while, I found a good solution. I checked Deployment Properties table in CRM and found that ADSdkRootDomain and ADWebApplicationRootDomain values were pointed to LIVE server URL. Updating those values with Development Server URL solved the above issue. (Please refer to reference link for detail information.)

To check server URL:
USE [MSCRM_CONFIG]
SELECT ColumnName, NVarCharColumn FROM DeploymentProperties
WHERE ColumnName IN ('ADSdkRootDomain', 'ADWebApplicationRootDomain', 'AsyncSdkRootDomain')

To update server URL:
USE [MSCRM_CONFIG]
UPDATE DeploymentProperties
SET NVarCharColumn = '[servername]:[port]'
WHERE ColumnName IN ('ADSdkRootDomain', 'ADWebApplicationRootDomain', 'AsyncSdkRootDomain')
Reference:
CRM 4.0 Client wont connect

Saturday, January 30, 2010

Unable to obtain DNS hostname of Active Directory domain controller with ntdsa object name


Last week, my colleague tried to unregistered plug-in using Plugin Registration Tool and got above error message.Since it was very generic error message, I tried to look detail error message in Event Viewer but have no luck to find it.

One of my colleagues suggested me to check whether log-in account had deployment manager role or not. I tried to open Deployment Manager MMC and got following error message.


Below is the detail error message I got from CRM trace log.
System.DirectoryServices.ActiveDirectory.ActiveDirectoryOperationException: Unable to obtain DNS hostname of Active Directory domain controller with ntdsa object name "CN=NTDS Settings, CN=[DOMAIN CONTROLLER], CN=Servers, CN=Default-First-Site-Name, CN=Sites, CN=Configuration, DC=[CRM SERVER], DC=[DOMAIN NAME], DC=org, DC=sg".
Resolution:
When we unregistered a plug-in, CRM tried to query login user credential and role from AD. But Active Domain Controller information is pointed to Replicated AD which was no longer available at that moment. Then, plug-in registration process had assess right issue and we got "Server was unable to process request" error message.

Remove unused Domain Controller from "Active Directory Sites and Services" will help your problem. Please refer to Reference link for more information.

Reference:
Delete Failed DCs from Active Directory
Delete Failed DCs from AD.pdf

You have exceeded the maximum number of allowable lookup columns. (N:N Relationship)


Last week, my users report that they received an error when they click "Add Existing Contact" button in account entity. Below is the screen shot:


When I tried to trace the error, I found that it only happened on N:N Relationship entity. In Event Viewer, I found following error message:
Exception information:
    Exception type: IndexOutOfRangeException
    Exception message: You have exceeded the maximum number of allowable lookup columns.

Stack trace:    at Microsoft.Crm.Web.Controls.Lookup.Columns.Add(String displayName, String logicalName, String bindingName, String bindingAttribute, ColumnType type, String size)
   at Microsoft.Crm.Web.Controls.Lookup.LookupParser.ParseSectionColumns(XmlReader xmlReader, Object elementName)
   at Microsoft.Crm.Web.Controls.Lookup.LookupParser.Execute(String xml, Int32 objectType)
   at Microsoft.Crm.Web.Controls.Lookup.LookupDataPage.ConfigurePage()
   at Microsoft.Crm.Application.Controls.AppUIPage.OnPreRender(EventArgs e)
   at System.Web.UI.Control.PreRenderRecursiveInternal()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

Resolution: 
MS CRM has column limitation in Lookup View. You can show only up to 18 Columns. If you list more than that, IndexOutOfRangeException will occur when you add data to N:N Relationship entity. Remove columns from Lookup View will solve your problem.

Reference:
Account Lookup Column limit?

Monday, January 25, 2010

Download a file from URL in CRM text box



Sometimes you may want to download a file from URL showing in CRM text box. By default, when you click the URL, CRM will show content of the file in a new window (if it is a known MIME type). You can click 'Save As' from File menu to save the file. But, it is not very convenient for user to go through the whole process.  

Here is the solution:

  1. Create an ASP page (download.asp) and place it under ISV folder
  2. Attach Click Event on Text Box to download a file

Download.asp
<%@Language="VBScript"%>
<%Option Explicit%>
<%Response.Buffer = True%>

<%
On Error Resume Next

Dim strPath
strPath = CStr(Request.QueryString("file"))

'-- do some basic error checking for the QueryString
If strPath = "" Then
Response.Clear
Response.Write("No file specified.")
Response.End
ElseIf InStr(strPath, "..") > 0 Then
Response.Clear
Response.Write("Illegal folder location.")
Response.End
ElseIf Len(strPath) > 1024 Then
Response.Clear
Response.Write("Folder path too long.")
Response.End
Else
Call DownloadFile(strPath)
End If

Private Sub DownloadFile(file)
'-- Detect HTTP/HTTPS
Dim objRegExp
Set objRegExp = new RegExp
objRegExp.Pattern = "^h?[t|f]tps?://"
objRegExp.IgnoreCase = True
objRegExp.Global = True 
If objRegExp.Test(file) = True Then
objRegExp.Pattern = "([A-Za-z]{3,9})://([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((/[-\+~%/\.\w]+)?\??([-\+=&;%@\.\w]+)?#?([\w]+)?)?"
Dim matches
Set matches = objRegExp.Execute(file)
file = matches(0).SubMatches(5)
End If
'set absolute file location
Dim strAbsFile
strAbsFile = Server.MapPath(file)
'-- create FSO object to check if file exists and get properties
Dim objFSO
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
'-- check to see if the file exists
If objFSO.FileExists(strAbsFile) Then
Dim objFile
Set objFile = objFSO.GetFile(strAbsFile)
'-- first clear the response, and then set the appropriate headers
Response.Clear
'-- the filename you give it will be the one that is shown
' to the users by default when they save
Response.AddHeader "Content-Disposition", "attachment; filename=" & objFile.Name
Response.AddHeader "Content-Length", objFile.Size
Response.ContentType = "application/octet-stream"
Dim objStream
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Open
'-- set as binary
objStream.Type = 1
Response.CharSet = "UTF-8"
'-- load into the stream the file
objStream.LoadFromFile(strAbsFile)
'-- send the stream in the response
Response.BinaryWrite(objStream.Read)
objStream.Close
Set objStream = Nothing
Set objFile = Nothing
Else 'objFSO.FileExists(strAbsFile)
Response.Clear
Response.Write("File not found!")
End If
Set objFSO = Nothing
End Sub
%>

Attach On Click Event (place under Form_OnLoad event)
DownloadFile = function(fileURL)
{
var url= window.location.protocol + "//" + window.location.host + "/Download.asp?file=" + fileURL;  
window.open(url,'popup','width=100,height=50,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=100,top=200');
}
crmForm.all.[attribute_name].attachEvent('onclick', DownloadFile);

Syntax Example: 
1. Download.asp?file=/SampleFiles/test.txt
2. Download.asp?file=http://localhost/SampleFiles/test.txt

Download Link:
download.asp

Reference:
Downloading any file using ASP, FSO and the ADODB Stream object

Friday, January 15, 2010

Insufficient Permissions



Sometime you may encounter insufficient permissions warning in CRM applications while you are trying to assign ownership of a record to another user. If you try to look into Event Viewer, you will only get following error message:
Exception information:
    Exception type: TargetInvocationException
    Exception message: Exception has been thrown by the target of an invocation.

To resolve the issue, please follow the steps below:

1. Please Change
to
in web.config file of your CRM application. The file is usually located under C:/Program Files/Microsoft Dynamics CRM/CRMWeb folder.

2. Please test your application again. You will get following error message instead of Insufficient Permissions warning:



3. Please take PrivilegeId from your error message and follow the instruction from my previous post.

Reference:
CRM: SecLib::CheckPrivilege failed.
Tracing and Debugging error in CRM 4.0