Monday, February 10, 2014

.Net Winforms Basic BackgroundWorker Example

I use a BackgroundWorker a lot when working with WinForms in order to perform database or webservice calls without freezing the UI thread. Of course web services can be called using their built in asynchronously methods however when you have a separate data access or model layer, these asynchronous methods are not readily available. 

On a new project I was working on, I was looking for a basic example online for quick reference but there wasn't one amongst the top search results so I thought I'd post one.

    using System.ComponentModel;
    using System.Threading;
    using System.Windows.Forms;

    public partial class MainForm : Form
    {
        private readonly BackgroundWorker backgroundWorker = new BackgroundWorker();

        public MainForm()
        {
            this.InitializeComponent();
            this.backgroundWorker.DoWork += this.BackgroundWorkerDoWork;
            this.backgroundWorker.RunWorkerCompleted += this.BackgroundWorkerCompleted;
            this.backgroundWorker.RunWorkerAsync("Passed Argument");
        }

        private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(1000);
            e.Result = e.Argument;
        }

        private void BackgroundWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show(e.Result.ToString());
            //// Shows "Passed Argument"
        }
    }

Keep in mind you can not run a background worker that is always running so you probably want to do an:
if (this.backgroundWorker.IsBusy)
{
    //// Perhaps do something like put the RunWork request in a Queue.
    //// Once RunWorkerCompleted is fired, Dequeue an kick off the background
    //// worker again.
}
else
{
    this.backgroundWorker.RunWorkerAsync("Passed Argument");
}
For example:
        private readonly Queue<string> queue = new Queue<string>();

        private void InitializeRunWorkerCompleted()
        {
            this.backgroundWorker.RunWorkerCompleted += (sender, e) =>
            {
                if (this.queue.Count > 0)
                {
                    this.backgroundWorker.RunWorkerAsync(this.queue.Dequeue());
                }
                
                //// Regular logic here.
            };
        }

Sunday, February 9, 2014

Easy .NET MVC Active Directory Attribute Based Authorization

Active Directory based authorization in .NET is fairly easy. Just throw an attribute on a controller as follows:
[Authorize (Roles="MyAdGroup")]
public class SettingsController : Controller
Sometimes though you do not want to hard code a role in an attribute as you may want to add or remove roles at will. You may also want to change the roles based on whether you are in production or not. I like to keep my Active Directory roles either in a database or a web.config file so that others can change authorization on the fly. In order to have greater control over your authorization roles you need to extend the AuthorizeAttribute and override AuthorizationCore. You also need to override HandleUnauthorizedRequest in order to have a custom redirect page.
    /// <summary>
    /// Redirects to the unauthorized page.
    /// </summary>
    public class AuthorizeSiteRedirect : AuthorizeAttribute
    {
        /// <summary>
        /// Authorization based on roles in web.config.
        /// </summary>
        /// <param name="httpContext" />The http context
        /// <returns>Whether a user is authorized.</returns>
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            //// In this example we use a web.config key.
            //// <add key="Authorization.site" value="Your comma separated Ad Group List"/>
            var roles = ConfigurationManager.AppSettings["Authorization.Site"]
            return roles.Split(',').ToList().Any(role => httpContext.User.IsInRole(role.Trim());
        }

        /// <summary>
        /// Redirects an unauthorized request to the unauthorized page.
        /// </summary>
        /// <param name="filterContext" />The filter context
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        { 
            filterContext.Result = new RedirectResult("~/Unauthorized");
        }
    }
}

You can now add more authorization levels for example an Admin level.
    /// <summary>
    /// Redirects to the unauthorized page.
    /// </summary>
    public class AuthorizeAdminRedirect : AuthorizeSiteRedirect
    {
        /// <summary>
        /// Authorizes a user based on active directory groups.
        /// </summary>
        /// <param name="httpContext" />The http context</param>
        /// <returns>Whether a user is authorized.</returns>
        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var roles = ConfigurationManager.AppSettings["Authorization.Site.Admin"]          
            return roles.Split(',').ToList().Any(role => httpContext.User.IsInRole(role.Trim());
        }
    }
}
Now all you have to do is add an attribute to your controller and you are done:
    /// <summary>
    /// Administration page for site settings.
    /// </summary>
    [AuthorizeAdminRedirect]
    public class SettingsController : Controller
Please be aware that when you store you active directory groups in a location like your config file you need to be sure to trim whitespace from your group name. Otherwise httpContext.User.IsInRole will not work accurately. For example if User1 is in group Group1 then User.IsInRole(" Group1") will return false however User.IsInRole("Group1") will return true.

Friday, February 7, 2014

Common.Net library methods: Querying Active Directory for users and groups

Finding the groups a user belongs to in active directory along with the members of that group is something that comes up a lot when .Net apps use Active Directory for authentication. Here are some common library methods to find members of a group or groups a member belongs to. This code requires a reference to System.DirectoryServices.AccountManagement.
    using System;
    using System.Collections.Generic;
    using System.DirectoryServices.AccountManagement;
    using System.Linq; 

    public class ActiveDirectoryGateway 
    {
        private readonly string domain;

        private readonly ContextType contextType;

        public ActiveDirectoryGateway(ContextType contextType, string domain)
        {
            this.contextType = contextType;
            this.domain = domain;
        }

        /// <summary>
        /// Retrieves a list of AD groups belonging to an AD User.
        /// </summary>
        /// <param name="user">The active directory user.</param>
        /// <returns>A list of AD groups the user belongs to.</returns>
        public virtual IEnumerable<string> FindGroups(string user)
        {
            var list = new List<string>();
            using (var context = new PrincipalContext(this.contextType, this.domain))
            {
                using (var userPrincipal = UserPrincipal.FindByIdentity(context, user))
                {
                    if (userPrincipal == null)
                    {
                        return list;
                    }

                    using (var results = userPrincipal.GetGroups())
                    {
                        list.AddRange(results.Select(result => result.Name));
                    }
                }
            }

            list.Sort();
            return list;
        }

        /// <summary>
        /// Lists members of group.
        /// </summary>
        /// <param name="grp">The AD group.</param>
        /// <returns>A list of AD members of the group.</returns>
        public virtual IEnumerable<string> FindMembersOfGroup(string grp)
        {
            var list = new List<string>();
            using (var context = new PrincipalContext(this.contextType, this.domain))
            {
                using (var group = GroupPrincipal.FindByIdentity(context, grp))
                {
                    if (group == null)
                    {
                        return list;
                    }

                    list.AddRange(group.GetMembers(true).Select(result => result.Name));
                }
            }

            return list;
        }
}

Common .Net Library Methods: Easy XML Serialization and deserialization

Serializing XML to an object of a given type and back has been very common in a lot of my projects. It may not be allowable when you are working on multiple projects in different groups to import a common class library so I thought I'd post some basic serialization code generic to most projects that I have had to use a lot lately.
        /// <summary>
        /// Deserializes an XML document into a given type.
        /// </summary>
        /// <typeparam name="T">The type to deserialize.</typeparam>
        /// <param name="xml"> The xml. </param>
        /// <returns> An object representative of the XML document. </returns>
        public T Deserialize<T>(string xml)
        {
            var xmlSerializer = new XmlSerializer(typeof(T));
            using (var reader = XmlReader.Create(new StringReader(xml)))
            {
                if (xmlSerializer.CanDeserialize(reader))
                {
                    return (T)xmlSerializer.Deserialize(reader);
                }
            }

            return default(T);
        }

/// <summary> /// Serializes an object into an XML document. /// </summary> /// <param name="path"> The path. </param> /// <param name="o"> The object to serialize. </param> public void Serialize(string path, object o) { using (var writer = new StreamWriter(path)) { new XmlSerializer(o.GetType()).Serialize(writer, o); } }

Thursday, February 6, 2014

WCF Logging raw soap requests.

To troubleshoot a contract filter mismatch or other WCF fault message being thrown from  third party consumers, here is the web.config settings that will show the raw soap requests to inspect if the caller is using a malformed request. Please be aware that the first thing to do when troubleshooting a contract filter mismatch is to ensure the web reference is up to date and running. This message can be very deceiving such as if you are calling a web service from a web service, and the latter web service is not running or running on a different virtual etc.

Make sure the first section is contained within System.Servicemodel. As you can see I do not show the opening tag tag.
 
 <!-------- within the system.servicemodel element -------->
 <diagnostics>      
      <messageLogging
           logEntireMessage="true"
           logMalformedMessages="true"           
           logMessagesAtServiceLevel="false"
           logMessagesAtTransportLevel="true"
            maxSizeOfMessageToLog="65535000" 
      maxMessagesToLog="500" />
    </diagnostics>
  </system.serviceModel>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true" >
        <listeners>
          <add name="xml"/>
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="xml"/>
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add name="xml"
           type="System.Diagnostics.XmlWriterTraceListener"
           initializeData="C:\test\Traces.svclog" />
    </sharedListeners>
  </system.diagnostics>