tirsdag den 21. oktober 2014

Parameter "AssemblyFiles" has invalid value

Issue

When  referencing an assembly the compiler fails with this error

 error MSB3248: Parameter "AssemblyFiles" has invalid value "C:\CruiseControl\...\myAssembly.dll". Culture is not supported. [C:\CruiseControl\Projects\TemplateSite\WebApp\WebApp.csproj]  
 error MSB3248: Parameter name: name [C:\CruiseControl\..\WebApp.csproj]  
 myAssembly is an invalid culture identifier. [C:\CruiseControl\..\WebApp.csproj]  

Cause

This is caused by an invalid value in the AssemblyCulture attribute in the Assembly.cs

 [assembly: AssemblyCulture("myInvalidValue")]  

Solution

Set the AssemblyCulture to a valid value e.g. en-US or da-DK or just set it to default


 [assembly: AssemblyCulture("")]  

torsdag den 16. oktober 2014

How to prevent NAnt task returning code 3 to fail full buildprocess

Issue

The issue is that my buildscript fails because a task returns a code different from 0 even though the executable succeeds.

The buildscript contains a task that copies my website to a development server.
I use RoboCopy.exe to do this.

The task looks like this

  <target name="upload" depends="release"> 
   <exec 
    program="${robocopy}" 
    commandline=".\Website ${devroot}\www ${robocopyUploadArgs}" 
           /> 
  </target> 

However RoboCopy has a set of codes it can return depending on how many files were copied. See http://ss64.com/nt/robocopy-exit.html for more info.
Here it tells that a return value below 9 is a success.

Solution

By specifying to parameters; failonerror="false" and resultproperty="return.code" it is possible to tell NAnt that we dont want to fail on error but want the exit-code to be put in a property named return.code. Following a simple <if> task enables us to test and fail if the return code is larger than 8.
The script now looks like this (remember to add property named return.code)

torsdag den 18. september 2014

Sublayout caching disabled when sublayout is hidden by serverside code

Issue

I have a leftmenu that can be configured on the page-template to be hidden using a Sitecore field. When the editor sets a checkmark in the HideLeftMenu field the placeholder containing the sc:Sublayout control is hidden. This seems to result in that caching of the Sublayout is ignored.
The code looks like this

Markup

 <asp:PlaceHolder runat="server" ID="phLeftColumn">  
   <div class="col-sm-4 col-md-3">  
     <asp:ContentPlaceHolder runat="server" ID="cphPageContentLeft">  
       <dom:Sublayout runat="server" ID="submenu" Path="/layouts/LeftMenu.ascx" Cacheable="True" VaryByData="True" />  
     </asp:ContentPlaceHolder>  
   </div>  
 </asp:PlaceHolder>  

Code behind

 phLeftColumn.Visible = Sitecore.Context.Item["HideLeftMenu"] != "1";   

lørdag den 23. august 2014

Automatic Multisite Handling By Traversing Sitecore Tree

Add automatic multisite handling to your Sitecore site by traversing the Sitecore Tree to find the available sites. You can decide to use the site name as subdomain or you can specify a hostName in a Sitecore field on the site-item.


 

 

 

 

lørdag den 18. december 2010

MVC 3

I have just installed VS 2010 and MVC3.
I like the idea that abstraction layers now is part of the programming interface and that it is now possible to test your website using unit testing. Hurra for that... But.. and I think there is a big but.

I have just created a new web application using the MVC 3 template.
This template creates a small site with login/logout/register functionality out of the box. Very nice.

But what I dont fancy at all is the fact that there is a lot of late bound code (Code that is resolved at Runtime).

Examples on late bound code:
1. ViewBag.Title (Set in Views and used in _layout)
2. @Html.Partial("_LogOnPartial") used in _layout
3. @Html.ActionLink("Register", "Register") (I know this is not likely to change name but what if it does and you forget to change it just one place?)

All these issues results in runtime errors but could in the "good" old days have been caught by the compiler by doing following..

1. Using Page.Title or a extension method on Page
2. Using @Register and tag
3. This is pretty much the same but would not resolve in a runtime error but a "not found" page. I dont know which one is better

Am I the only one with this concern?

lørdag den 11. december 2010

Asymmetric Encryption on Client and Server

In order to use SSL you need at dedicated server. This is not cost effective so the solution is to encrypt the stuff yourself.

This can be done but is not an easy job due to the fact that you need to find code that can encrypt and decrypt both on client and server.

You need this server class

 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Web;  
 using System.Security.Cryptography;  
 using System.IO;  
 public class Cryptography  
 {  
   public static RSACryptoServiceProvider rsa;  
   public static void AssignParameter()  
   {  
     const int PROVIDER_RSA_FULL = 1;  
     const string CONTAINER_NAME = "SpiderContainer";  
     CspParameters cspParams;  
     cspParams = new CspParameters(PROVIDER_RSA_FULL);  
     cspParams.KeyContainerName = CONTAINER_NAME;  
     cspParams.Flags = CspProviderFlags.UseMachineKeyStore;  
     cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";  
     rsa = new RSACryptoServiceProvider(cspParams);  
   }  
   public static string EncryptData(string data2Encrypt)  
   {  
     AssignParameter();  
     StreamReader reader = new StreamReader(System.Web.HttpContext.Current.Server.MapPath(@"App_Data\publickey.xml"));  
     string publicOnlyKeyXML = reader.ReadToEnd();  
     rsa.FromXmlString(publicOnlyKeyXML);  
     reader.Close();  
     //read plaintext, encrypt it to ciphertext  
     byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt);  
     byte[] cipherbytes = rsa.Encrypt(plainbytes, false);  
     return Convert.ToBase64String(cipherbytes);  
   }  
   public static void AssignNewKey()  
   {  
     AssignParameter();  
     //provide public and private RSA params  
     StreamWriter writer = new StreamWriter(System.Web.HttpContext.Current.Server.MapPath(@"App_Data\privatekey.xml"));  
     string publicPrivateKeyXML = rsa.ToXmlString(true);  
     writer.Write(publicPrivateKeyXML);  
     writer.Close();  
     //provide public only RSA params  
     writer = new StreamWriter(System.Web.HttpContext.Current.Server.MapPath(@"App_Data\publickey.xml"));  
     string publicOnlyKeyXML = rsa.ToXmlString(false);  
     writer.Write(publicOnlyKeyXML);  
     writer.Close();  
   }  
   public static string DecryptData(string data2Decrypt)  
   {  
     if (data2Decrypt == null)  
       return null;  
     AssignParameter();  
     byte[] getpassword = Convert.FromBase64String(data2Decrypt);  
     StreamReader reader = new StreamReader(System.Web.HttpContext.Current.Server.MapPath(@"App_Data\privatekey.xml"));  
     string publicPrivateKeyXML = reader.ReadToEnd();  
     rsa.FromXmlString(publicPrivateKeyXML);  
     reader.Close();  
     //read ciphertext, decrypt it to plaintext  
     byte[] plain = rsa.Decrypt(getpassword, false);  
     return System.Text.Encoding.UTF8.GetString(plain).Replace(UniqueGuidForEncryption.ToString(), "");  
   }  
 }  

And you need this set of javascript files and a reference to them

http://www.e-keys.dk/js/RSA/jsbn.js
http://www.e-keys.dk/js/RSA/prng4.js
http://www.e-keys.dk/js/RSA/rng.js
http://www.e-keys.dk/js/RSA/rsa.js
http://www.e-keys.dk/js/RSA/base64.js

Add this code to the OnLoad

 if( !System.IO.File.Exists(Server.MapPath("App_Data/publickey.xml")) || Request.QueryString["newkey"] != null )  
   Cryptography.AssignNewKey();  


This will create the public and the private key xml file that is used to encrypt/decrypt the data before it is sent in the postback.

Add this script to you page

 function do_encrypt(val) {  
   if (val.length == 0)  
     return val;  
   var rsa = new RSAKey();  
   rsa.setPublic('', '');  
   var res = rsa.encrypt(val);  
   return hex2b64(res);  
 }  
 function hashAndEncrypt(val) {  
   var rsa = new RSAKey();  
   rsa.setPublic('', '');  
   var res = rsa.encrypt(val);  
   return hex2b64(res);  
 }  

and add this two methods to your code behind

 protected String RSAModulus  
 {  
   get  
   {  
     XmlDocument doc = new XmlDocument();  
     doc.Load(Server.MapPath("App_Data/publickey.xml"));  
     return BitConverter.ToString(Convert.FromBase64String(doc.SelectSingleNode("RSAKeyValue/Modulus").InnerText)).Replace("-", "");  
   }  
 }  
 protected String RSAExponent  
 {  
   get  
   {  
     XmlDocument doc = new XmlDocument();  
     doc.Load(Server.MapPath("App_Data/publickey.xml"));  
   return BitConverter.ToString(Convert.FromBase64String(doc.SelectSingleNode("RSAKeyValue/Exponent").InnerText)).Replace("-", "");  
   }  
 }  

This is needed because the client side encryption needs hexa-decimal keys and the server side uses Base64Strings.

You need to call DataBind() as the last thing in OnLoad.

The last thing you need to do is encrypt the content of the textboxes. This is done in the click event of the button like this

 document.getElementById("textbox-id").value = do_encrypt(document.getElementById("textbox-id").value)  


You need to set the value for one and each of the textboxes that you need encrypted.

On the server you just call Cryptography.DecryptData(text) to decrypt the content of the textbox.

This should be it.

mandag den 14. december 2009

Cookies in ASP.NET

I always find myself struggling with Cookies in asp.net.

I always fail to add the cookies the right way or to remove them.
I always tend to open old projects to see how I coped then.
But most of the project are filled with different tries to get the cookie stuff to work so it doesnt help much.

There are three issues that we are dealing with

1. Adding a cookie that never expires
2. Adding a cookie that expires with a day or so
3. Removing a cookie

1 + 2. The first mistake I make is trying to add a cookie to the Request.Cookie collection which obviously doesnt work due to the fact it is a collection comming from the server.

All cookie should be added to the Response.Cookie collection.
For adding expiration you just set the Expire property on the HttpCollection before adding the cookie. For a cookie that never expires you set Expire = DateTime.MaxValue;

3. Removing a cookie is not done "just" by removing the cookie from the request cookie collection. You also need to add a new cookie with the same name as the one you want to remove and setting Expire to yesterday to Response.Cookie collection . This forces the cookie to expire the next time the client requests the page.

 HttpCookie c = new HttpCookie(CookieName, "false");  
 c.Expires = DateTime.Now.AddDays(-1);  
 Response.Cookies.Add(c);  
 Request.Cookies.Remove(CookieName);   

Very simple but a hell of a job if you forget just one of them.