Tuesday, August 18, 2009

Detecting when .netTiers is interrogating a stored procedures

During code generation using the .netTiers templates the stored procedures will be called to determine their outputs. If the stored procedures don't return the correct results netTiers will only provide IDataReader as a return type rather than a strongly typed object.

To resolve this use the SQL user_name() function to determine if netTiers is calling the stored procedure and return data that will generate the correct structure.

-- Check if netTiers is trying to discover how this stored proc works and return a dummy record with the expected result set.
 IF user_name() IS null
 BEGIN
  -- Return dummy data to netTiers with the expected data types
  SELECT TOP 1 * FROM [dbo].[SampleView]

  SELECT 1 as TotalRowCount

  RETURN
 END

It will pay to check your custom stored procedure works with FMTONLY. NetTiers will call the stored procedure with FMTONLY on and null for all of the parameters.

SET FMTONLY ON;

EXEC dbo.custom_sp_MyTestSproc @param1 = NULL, @param2 = NULL, @param3 = NULL

SET FMTONLY OFF;

I found this caused issues when using temporary tables. I.e.:

Msg 208, Level 16, State 0, Procedure custom_sp_StoredProcedureName, Line 160
Invalid object name '#TheTempTable'.

If this is the case you can turn FMTONLY OFF in the in stored procedure before creating the temporary table and then back on again afterward. E.g.

-- ...
DECLARE @fmtonlyon bit
SELECT @fmtonlyon = 0
IF 1 = 0 
BEGIN
 --This block will only be reached when FMTONLY is ON
 SELECT @fmtonlyon = 1
 SET FMTONLY OFF
END
    
CREATE TABLE #TheTempTable
  ( 
   -- ...
  )

--USE test here as FMTONLY will be disabled
IF @fmtonlyon = 1 SET FMTONLY ON

Cannot insert an explicit value into a timestamp column.

When creating a temporary table variable I needed to store a timestamp that was being sourced from another table. When attempting to insert that values into the timestamp column to following error message appeared:

Cannot insert an explicit value into a timestamp column. Use INSERT with a column list to exclude the timestamp column, or insert a DEFAULT into the timestamp column.

According to books online:

A nonnullable timestamp column is semantically equivalent to a binary(8) column. A nullable timestamp column is semantically equivalent to a varbinary(8) column.

So changing the column in the temporary table from timestamp to binary(8) resolved the issue.

Tuesday, August 11, 2009

Sitecore 6 - CryptographicException: Padding is invalid and cannot be removed.

I've been getting the following exception from sitecore fairly frequently from my local IIS.

[CryptographicException: Padding is invalid and cannot be removed.]
   System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast) +2910
   System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) +286
   System.Security.Cryptography.CryptoStream.FlushFinalBlock() +51
   System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo) +318
   System.Web.Security.FormsAuthentication.Decrypt(String encryptedTicket) +290
   Sitecore.Security.Authentication.AuthenticationHelper.GetCurrentUser() +473
   Sitecore.Security.Authentication.AuthenticationHelper.GetActiveUser() +17
   Sitecore.Security.Authentication.AuthenticationProvider.GetActiveUser() +21
   Sitecore.Security.Authentication.AuthenticationManager.GetActiveUser() +39
   Sitecore.Context.get_User() +17
   Sitecore.DateUtil.ParseTimeSpan(String time) +51
   Sitecore.Configuration.Settings.GetTimeSpanSetting(String name, TimeSpan defaultValue) +106
   Sitecore.Caching.CacheManager.InitializeScavenging() +70
   Sitecore.Caching.CacheManager..cctor() +92

I've tracked it back to the following sitecore code in Sitecore.Security.Authentication.AuthenticationHelper.GetCurrentUser():

protected User GetCurrentUser()
{
    HttpContext current = HttpContext.Current;
    if (current != null)
    {
        IPrincipal user = HttpContext.Current.User;
        if (user == null)
        {
            HttpCookie cookie = current.Request.Cookies[FormsAuthentication.FormsCookieName];
            if ((cookie != null) && !string.IsNullOrEmpty(cookie.Value))
            {
                FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
                if (!string.IsNullOrEmpty(ticket.Name))
                {
                    return this.GetUser(ticket.Name, true);
                }
            }
            return null;
        }
        // omitted code
        return null;
    }
    // omitted code
    return null;
}

The issue around line 473 (12 in the sample above) is in decrypting the value of the Forms Authentication cookie.

Possible Solution 0

Install Sitecore CMS 6.3.0 rev.100716 or higher. From the release notes:

When receiving an invalid authentication cookie (which may occur when switching between Sitecore instances in the IIS or when running a load balanced setup where each web application uses different machine keys), Sitecore would sometimes throw a "CryptographicException: Padding is invalid and cannot be removed" error and return a "500 Server Error" response to all requests hitting the web server. The CryptographicException will still be written to the log file to inform about improper configuration, while all requests passing invalid cookies are processed under the context of Anonymous user. (320777)

Possible Solution 1

If you are hosting with IIS7 try making adding the following to the global.asax (found at Running Sitecore 6 on Windows 7’s IIS but equally applies to Windows Server 2008):

public void Application_Start() {
      System.Security.Cryptography.RSACryptoServiceProvider.UseMachineKeyStore = true;
      System.Security.Cryptography.DSACryptoServiceProvider.UseMachineKeyStore = true;
  }

Possible Solution 2

Try clearing out the browsers cookies - in particular the Forms Authentication Cookie that ASP.NET creates.

Possible Solution 3

Try restarting/recycling the application pool in IIS.

Possible Solution 4

This may be applicable if you are running multiple versions of the site locally on different ports. Change these settings so that they are unique for each of your local sites.

Change your web.config Authentication Form name, which is used for the cookie name. Below the ".XXXXXXXX" is usually defaults to ".ASPXAUTH".

<authentication mode="None">
   <forms name=".XXXXXXXX" cookieless="UseCookies"/>
</authentication>

Add a unique machineKey in the web.config system.web node.

<system.web>
  <!-- Machine key attributes -->
  <machineKey validationKey="3SAMPLE122EA7A3F74BBEAD8450366243D982677F5243660BBAE3281839E013C2CEE7CF18BB85DC00C207E861B309FB075C51F74DFEB5FC4B72E01277610SAMPLE" 
       decryptionKey="060B29B5E05ESAMPLE42884148138D39B2ED861755A5E64AE370F6473E8SAMPLE" 
       validation="SHA1" decryption="AES" />

</system.web>

Possible Solution 5

Day to day I may have multiple separate instances of Sitecore running on my local IIS for developing for different clients. I'll typically be accessing them as http://localhost:8080 or http://127.0.0.1:8080 with different ports depending on the instance.

I often seem to come across the padding exception when switching between the multiple sites (by just changing the port).

My new theory is to setup a mock domain in my hosts file and change the site binding in IIS to use this new binding on port 80. I then use the new mock domain to access Sitecore from the browser.

See Also

Monday, August 10, 2009

Obsolete - HttpContext.Current.Request.Browser.JavaScript

'System.Web.Configuration.HttpCapabilitiesBase.JavaScript' is obsolete: 'The recommended alternative is the EcmaScriptVersion property. A Major version value greater than or equal to 1 implies JavaScript support. http://go.microsoft.com/fwlink/?linkid=14202'

Try using the following instead:

(HttpContext.Current.Request.Browser.EcmaScriptVersion.Major > 1)

See also: HttpBrowserCapabilities.EcmaScriptVersion

Thursday, August 6, 2009

Salesforce APEX "System.QueryException: List has no rows for assignment to SObject"

The assignment to a single object requires the query to return a single row, if the query may not return a row, you should assign it to an array, then examine the size of the array to determine if you got a row or not, and take the appropriate action.
http://forums.sforce.com/sforce/board/message?board.id=apex&thread.id=3470

Salesforce APEX "Log filter in effect"

When trying to debug a managed package I'm seeing the following message in the Debug Logs.

Log filter in effect. Executing code from installed managed application

This is "by design" and means you can't see debug level log messages - http://community.salesforce.com/sforce/board/message?board.id=apex&message.id=12460#M12460

Update 13/02/2012
The idea Display managed package logs in subscriber orgs is marked as delivered but doesn't actually say what was delivered. I've raised the following question on the forum to try and find out - Logging from a deployed managed package.