tag:blogger.com,1999:blog-3271991866929733302024-02-07T05:06:45.989+01:00About Sitecore and .NETUnknownnoreply@blogger.comBlogger5125tag:blogger.com,1999:blog-327199186692973330.post-16263825947717044942016-10-27T17:07:00.002+02:002016-10-27T17:07:48.473+02:00Easily prevent cookie replay attacks in SitecoreImagine you have a site which is (partially) protected and you use forms authentication to let your users gain access. Then you might be vulnerable to a cookie replay attack. Apparently this is <a href="https://www.vanstechelman.eu/content/cookie-replay-attacks-in-aspnet-when-using-forms-authentication" target="_blank">a weakness that has been around in the .NET framework for ages</a>. That article explains very well what a cookie replay attack is. There is also an old <a href="https://support.microsoft.com/en-us/kb/900111" target="_blank">Microsoft Knowledge Base article</a> on that subject.<br />
<br />
To protect your site against such a cookie replay attack, you must implement the following security measures:<br />
<ul>
<li>When signing in, set a boolean session variable to true.</li>
<li>When signing out, set the session variable to false.</li>
<li>Check the session variable on each request. If the value is false (or non-existing), return not authenticated.</li>
</ul>
Now to make your life and those of your team easier, you can create a custom forms authentication provider for Sitecore which implement the security measures mentioned above. With this custom provider, you nor any other developer on the team must think about this weakness. As long as the custom provider is in place, that is.<br />
<div>
<br /></div>
<div>
Developers can keep doing what they did before:</div>
<div>
<ul>
<li>On login, call: <span style="font-family: "courier new" , "courier" , monospace;">AuthenticationManager.Login(username, password);</span></li>
<li>On logout, call: <span style="font-family: "courier new" , "courier" , monospace;">AuthenticationManager.Logout();</span></li>
</ul>
</div>
<div>
<br /></div>
<div>
Time to bring the custom provider!<br />
<br /></div>
<div>
<pre class="line-numbers brush: csharp"><code class="language-csharp">using System;
using System.Linq;
using System.Web;
using Sitecore;
using Sitecore.Security.Accounts;
using Sitecore.Security.Domains;
namespace Custom.Security.Authentication
{
public class CustomFormsAuthenticationProvider : Sitecore.Security.Authentication.FormsAuthenticationProvider
{
private const string LoggedInSessionKey = "LoggedIn";
private static readonly string[] SitesToSkip = new[] { "shell", "login", "admin" };
public override bool Login(User user)
{
if (!base.Login(user))
{
return false;
}
if (IsValidSite())
{
HttpContext.Current.Session[LoggedInSessionKey] = true;
}
return true;
}
public override bool Login(string userName, bool persistent)
{
if (!base.Login(userName, persistent))
{
return false;
}
if (IsValidSite())
{
HttpContext.Current.Session[LoggedInSessionKey] = true;
}
return true;
}
public override bool Login(string userName, string password, bool persistent)
{
if (!base.Login(userName, password, persistent))
{
return false;
}
if (IsValidSite())
{
HttpContext.Current.Session[LoggedInSessionKey] = true;
}
return true;
}
public override void Logout()
{
if (HttpContext.Current.Session != null && HttpContext.Current.Session[LoggedInSessionKey] != null)
{
HttpContext.Current.Session.Remove(LoggedInSessionKey);
}
base.Logout();
}
public override User GetActiveUser()
{
if (!IsValidSite() || IsMarkedAsLoggedInOrNoSession())
{
return base.GetActiveUser();
}
return Context.Domain.GetAnonymousUser() ?? Domain.GetDefaultAnonymousUser();
}
private static bool IsValidSite()
{
return !SitesToSkip.Contains(Sitecore.Context.GetSiteName(), StringComparer.OrdinalIgnoreCase);
}
public static bool IsMarkedAsLoggedInOrNoSession()
{
return HttpContext.Current == null
|| HttpContext.Current.Session == null
|| HttpContext.Current.Session[LoggedInSessionKey] != null;
}
}
}
</code></pre>
<br /></div>
<div>
Plain and simple. With one caveat. Sitecore calls <span style="font-family: "courier new" , "courier" , monospace;">GetActiveUser()</span> several times when signing in to the Sitecore backend. It does not call any of the login methods. On recommendation of Sitecore Support, we just call <span style="font-family: "courier new" , "courier" , monospace;">base.GetActiveUser()</span> if we are on any of the backend sites.<br />
<br />
The only thing to do, is to let Sitecore know about your custom authentication provider. Adjust the web.config as follows:<br />
<br />
<pre class="line-numbers brush: xml"><code class="language-xml"><authentication defaultProvider="forms">
<providers>
<clear />
<add name="forms" type="Custom.Security.Authentication.CustomFormsAuthenticationProvider, Custom" />
</providers>
</authentication>
</code></pre>
<br />
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-327199186692973330.post-17475776463415475362016-10-04T08:00:00.000+02:002016-10-04T14:43:41.076+02:00Dependency injection in a modular architecture like Sitecore HelixRegistering all your types using your favorite IoC container in a modular architecture based solution (cfr Sitecore Helix) might become time consuming and cumbersome over time. Not to mention the times you've developed a new feature and published it to find out you've forgotten to register your new types! That means an extra site recycle...<br />
<br />
To overcome this you can implement a system which registers your types automatically. No more forgetting, no more adding project references; just develop and play! The sample code uses Autofac, but you can use any other IoC container you like.<br />
<br />
<h3>
Setting up the basics</h3>
First thing to do, is to create an interface that will be used in every module and scanned for when registering the types.<br />
<pre class="line-numbers brush: csharp"><code class="language-csharp">using Autofac;
public interface IModuleInitializer
{
ContainerBuilder Register(ContainerBuilder builder);
}
</code></pre>
<br />
Having defined the interface, let's implement a default implementation for it. This one will do all of the registering work for every type in each module in your solution.<br />
<br />
What it does:<br />
<ol>
<li>First it gets the current assembly.</li>
<li>Then it scans the assembly for types where the names ends with a few pre-defined keywords. This is a convention that you must agree upon with your team.</li>
<li>The types it finds will be registered as the interface it implements. Any other configuration is done as well, like single instance or auto-wiring properties.</li>
<li>The last step is to register the controllers and API controllers.</li>
</ol>
<pre class="line-numbers brush: csharp"><code class="language-csharp">using System;
using Autofac;
using Autofac.Integration.Mvc;
using Autofac.Integration.WebApi;
public class DefaultModuleInitializer : IModuleInitializer
{
public virtual ContainerBuilder Register(ContainerBuilder builder)
{
var assembly = GetType().Assembly;
builder.RegisterAssemblyTypes(assembly)
.Where(
x =>
x.Name.EndsWith("Repository", StringComparison.OrdinalIgnoreCase) ||
x.Name.EndsWith("Factory", StringComparison.OrdinalIgnoreCase) ||
x.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase))
.AsImplementedInterfaces()
.SingleInstance()
.PropertiesAutowired();
builder.RegisterControllers(assembly).PropertiesAutowired();
builder.RegisterApiControllers(assembly).PropertiesAutowired();
return builder;
}
}
</code></pre>
<br />
You may already have noticed that the method is marked as <span style="font-family: "courier new" , "courier" , monospace;">virtual</span>. This comes in handy if you need to add types that don't match the predicate, or in case you need to overwrite the default implementation.<br />
<br />
<h3>
Add it to every module</h3>
In order to have it register your module's types, you'll need to create a class which inherits the <span style="font-family: "courier new" , "courier" , monospace;">DefaultModuleInitializer</span> in each module. You can also implement <span style="font-family: "courier new" , "courier" , monospace;">IModuleInitializer</span> instead if your module is way off your default implementation. Remember to have one implementation in each module!<br />
<br />
In most cases your class will look like this:<br />
<pre class="line-numbers brush: csharp"><code class="language-csharp">public class ModuleInitializer : DefaultModuleInitializer
{
}
</code></pre>
<br />
However, if you need extra registrations, then it might look like this:<br />
<pre class="line-numbers brush: csharp"><code class="language-csharp">using Autofac;
public class ModuleInitializer : DefaultModuleInitializer
{
public override ContainerBuilder Register(ContainerBuilder builder)
{
builder = base.OnContainerInitializing(builder);
builder.RegisterType<usereventhandler>().AsSelf().PropertiesAutowired();
return builder;
}
}
</usereventhandler></code></pre>
<br />
<h3>
Let your IoC container know!</h3>
Now it's time to actually register your types. The <span style="font-family: "courier new" , "courier" , monospace;">GetModuleInitializers()</span> method searches for classes implementing <span style="font-family: "courier new" , "courier" , monospace;">IModuleInitializer</span> by scanning all bin files starting with your custom namespace. Each class found is then instantiated and returned. The <span style="font-family: "courier new" , "courier" , monospace;">Register()</span> method will loop all found <span style="font-family: "courier new" , "courier" , monospace;">IModuleInitializer</span>s and call its <span style="font-family: "courier new" , "courier" , monospace;">Register()</span> method. Et voila, you're done!<br />
<pre class="line-numbers brush: csharp"><code class="language-csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Http;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using Autofac.Integration.Web;
using Autofac.Integration.WebApi;
using Sitecore.IO;
public static class AutofacConfiguration
{
public static IContainerProvider Register()
{
var builder = new ContainerBuilder();
var modules = GetModuleInitializers().ToList();
foreach (var moduleInitializer in modules)
{
moduleInitializer.Register(builder);
}
var container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
return new ContainerProvider(container);
}
public static IEnumerable<IModuleInitializer> GetModuleInitializers()
{
var assemblyFilenames = FileUtil.GetFiles(FileUtil.MapPath("/bin"), "YourNamespace.*.dll", false);
var moduleInitializerType = typeof(IModuleInitializer);
foreach (var assemblyFileName in assemblyFilenames)
{
var assembly = Assembly.LoadFrom(assemblyFileName);
var initializerTypes = assembly.GetTypes().Where(x => moduleInitializerType.IsAssignableFrom(x) && !x.IsInterface);
foreach (var initializerType in initializerTypes)
{
yield return Activator.CreateInstance(initializerType) as IModuleInitializer;
}
}
}
}
</code></pre>
<br />Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-327199186692973330.post-65133420066891863952016-03-03T09:15:00.000+01:002016-09-30T16:57:27.557+02:00E-mail address validation in Sitecore WFFMThe e-mail validation that comes with WFFM marks e-mail addresses with top level domains of 5 characters or more as invalid (for instance sitecoredeveloper@some.education). Luckily, you can change the regular expression that is being used. Open the Sitecore Client and go to <span style="font-family: "courier new" , "courier" , monospace;">/sitecore/system/Modules/Web Forms for Marketers/Settings/Validation/E-mail</span> and alter the Validation Expression field.<br />
<br />
The default value is: <span style="font-family: "courier new" , "courier" , monospace;">^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$</span><br />
Change it to: <span style="font-family: "courier new" , "courier" , monospace;">^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$</span><br />
<br />
The difference is that the last part of the e-mail address has no limitations on the number of characters.<br />
<div>
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-327199186692973330.post-86083547559927188182015-12-07T21:13:00.002+01:002015-12-08T12:30:37.220+01:00Incorrect data is saved for list fields in Sitecore WFFM MVCWhen you use Sitecore WebForms for Marketers MVC, then chances are you run into the problem that the chosen values for list fields are saved incorrectly or not at all when submitting the form. For instance, when I submitted a form, no data was saved for drop lists and System.String[] was saved as value for a date picker. At the time of writing, this applies to WFFM 2.4+.<br />
<br />
The solution is two-folded. First of all, there is <a href="https://kb.sitecore.net/articles/311218" target="_blank">a known issue</a> described on the Sitecore Knowledge Base. The remedy is to download the support dll and to make changes to some WFFM files and items. Make sure you pick the right Sitecore version.<br />
<br />
But even after applying that fix, I still ran into the same issue. It turned out that Sitecore WFFM MVC relies on the default dependency resolver and that I've set that resolver according to the <a href="http://docs.autofac.org/en/latest/integration/mvc.html#set-the-dependency-resolver" target="_blank">Autofac documentation</a> as follows:<br />
<br />
<pre class="line-numbers brush: csharp"><code class="language-csharp">var builder = new ContainerBuilder();
// Register your types
// ...
var container = builder.Build();
// Set the resolver to Autofac
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
</code></pre>
<br />
WFFM expects to find the Sitecore dependency resolver there, but finds Autofac instead. A resolver that doesn't know anything about the WFFM types. To overcome that issue, you must register the WFFM types yourself.<br />
<br />
<pre class="line-numbers brush: csharp"><code class="language-csharp">// Sitecore WFFM
builder.RegisterType<Sitecore.Forms.Mvc.Controllers.ModelBinders.FieldBinders.DefaultFieldValueBinder>().AsSelf();
builder.RegisterType<Sitecore.Forms.Mvc.Controllers.ModelBinders.FieldBinders.CaptchaFieldBinder>().AsSelf();
builder.RegisterType<Sitecore.Forms.Mvc.Controllers.ModelBinders.FieldBinders.CheckboxFieldValueBinder>().AsSelf();
builder.RegisterType<Sitecore.Forms.Mvc.Controllers.ModelBinders.FieldBinders.DatePickerFieldValueBinder>().AsSelf();
builder.RegisterType<Sitecore.Forms.Mvc.Controllers.ModelBinders.FieldBinders.ListFieldValueBinder>().AsSelf();
</code></pre>
<br />
After that, data from list fields should be saved properly again!Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-327199186692973330.post-76294839696670808352015-12-02T12:18:00.000+01:002015-12-02T12:18:04.601+01:00Multilingual e-mail and date selector in Sitecore Web Forms for Marketers (WFFM)<h2>
Multilingual e-mails</h2>
Sitecore WFFM does not support multilingual e-mails out of the box. In other words, the e-mail sent will always be in the default language. To enable multilingual e-mails, open <span style="font-family: "courier new" , "courier" , monospace;">/sitecore/templates/Web Forms for Marketers/Form/Submit/Save Actions</span>, uncheck the “<span style="font-family: "courier new" , "courier" , monospace;">Shared</span>” checkbox and save.<br />
<br />
If you already have existing forms, then you will need to re-add all the save actions in each language for all forms. You must do this in a specific way: use the Form Designer inside Sitecore Client and only change the language in the form designer.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsXspffA8wSYS8kT_4u1SnZ6UN_FSiFyT5rJjHcxhG5TdNf8IMuDegkSPYJ9itL3_K2eHLkXGlLpWIKz-VP72XdL0i08d_yxoX0X92xg79etrtdxYLvam8whZRp-UhK_3DbU-hjPOZOHg/s1600/wffm_email-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="286" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsXspffA8wSYS8kT_4u1SnZ6UN_FSiFyT5rJjHcxhG5TdNf8IMuDegkSPYJ9itL3_K2eHLkXGlLpWIKz-VP72XdL0i08d_yxoX0X92xg79etrtdxYLvam8whZRp-UhK_3DbU-hjPOZOHg/s640/wffm_email-1.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvU-_87NKNzDmbG_SvP9fV6OeHMYKsQqQ9HmBsA8JGW3v8pB5B4XRxkZY64I2R9fQaTYbCpP9p43-W_4fTP7gpYlCUYYQIU6ifMIjq26RgqtkhdBY_jGr3DnVj-K7DVwdyo1B_1LOVqTw/s1600/wffm_email-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="243" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvU-_87NKNzDmbG_SvP9fV6OeHMYKsQqQ9HmBsA8JGW3v8pB5B4XRxkZY64I2R9fQaTYbCpP9p43-W_4fTP7gpYlCUYYQIU6ifMIjq26RgqtkhdBY_jGr3DnVj-K7DVwdyo1B_1LOVqTw/s640/wffm_email-2.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h2>
Multilingual date selector</h2>
When using a field of type 'Date' (or 'DateSelector'), you’ll get 3 dropdowns, with a label on top of it. These labels are in English.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiXUpKaMs-Ya0Zr0LlC_qgJ5vFwzCFC8cqKzSSNWQ4D-9222qFNjxX9ydrC48p1vUSkDzVUXfAsPhJ1BhAdTBXGxrJ420xhd-SE4xuOCWZCS8105wKUL6rUYpnzmWoSUe-WcZsZI_DV4o/s1600/wffm_date-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="57" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiXUpKaMs-Ya0Zr0LlC_qgJ5vFwzCFC8cqKzSSNWQ4D-9222qFNjxX9ydrC48p1vUSkDzVUXfAsPhJ1BhAdTBXGxrJ420xhd-SE4xuOCWZCS8105wKUL6rUYpnzmWoSUe-WcZsZI_DV4o/s640/wffm_date-1.png" width="640" /></a></div>
<br />
To add translations for these labels, do the following:<br />
<ol>
<li>Go to <span style="font-family: "courier new" , "courier" , monospace;">/sitecore/system/Dictionary</span></li>
<li>Create a new dictionary folder, e.g. WFM, just to keep things organized.</li>
<li>Create a dictionary entry for each label</li>
<ol>
<li>Use the following rule for the Key value: "<span style="font-family: "courier new" , "courier" , monospace;">{0}WFM: {1}</span>" (without the quotes)<br />where {1} is your key (i.e. 'Day' - 'Month' - 'Year')<br />and {0} is the first letter of the key in uppercase.</li>
<li>Enter a Phrase in each language</li>
</ol>
</ol>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkTfITl2aEtn9gVkvuyo3H5pQBBim7tlatdtP-_lDr3Rgp4ADo4i8XmR9V0VACRLkFJFgnXu0cdh-vGnllPSRygqQ4blkNa2jbYjiSGJA9HvoG2Cs8K4AHGelmUahf_yi1sqvIu19EYew/s1600/wffm_date-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="281" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkTfITl2aEtn9gVkvuyo3H5pQBBim7tlatdtP-_lDr3Rgp4ADo4i8XmR9V0VACRLkFJFgnXu0cdh-vGnllPSRygqQ4blkNa2jbYjiSGJA9HvoG2Cs8K4AHGelmUahf_yi1sqvIu19EYew/s640/wffm_date-2.png" width="640" /></a></div>
<div>
<br /></div>
Unknownnoreply@blogger.com0