How to use AD Attributes not represented in UserPrincipal, GroupPrincipal and ComputerPrincipal

Using System.DirectoryServices.AccountManagement comapred to just using System.DirectoryServices is way much simpler just look at these samples Active Directory and .NET 3.5/4.0 and Active Directory and .NET 2.0 clearly from those .NET3.5/4.0 is straightforward than the other but one thing is missing, exposing other attributes that are not represented in UserPrincipal, GroupPrincipal and ComputerPrincipal.

Using the old way you can just do this go get and set the attributes, its really that simple.

/// <summary>
/// This will retreive the specified poperty value from the DirectoryEntry object (if the property exists)
/// </summary>
/// <param name="oDE"></param>
/// <param name="sPropertyName"></param>
/// <returns></returns>
public string GetProperty(DirectoryEntry oDE, string sPropertyName)
{
    if (oDE.Properties.Contains(sPropertyName))
    {
        return oDE.Properties[sPropertyName][0].ToString();
    }
    else
    {
        return string.Empty;
    }
}

/// <summary>
/// This will test the value of the propertyvalue and if empty will not set the property
/// as AD is particular about being sent blank values
/// </summary>
/// <param name="oDE"></param>
/// <param name="sPropertyName"></param>
/// <param name="sPropertyValue"></param>
public void SetProperty(DirectoryEntry oDE, string sPropertyName, string sPropertyValue)
{
    //check if the value is valid, otherwise dont update
    if (sPropertyValue != string.Empty)
    {
        //check if the property exists before adding it to the list
        if (oDE.Properties.Contains(sPropertyName))
        {
            oDE.Properties[sPropertyName].Value = sPropertyValue;
            oDE.CommitChanges();
            oDE.Close();
        }
        else
        {
            oDE.Properties[sPropertyName].Add(sPropertyValue);
            oDE.CommitChanges();
            oDE.Close();
        }
    }
}

But with using System.DirectoryServices.AccountManagement the exposed attributes is limited to the following:

User Principal Properties Computer Principal Properties Group Principal Properties
AccountExpirationDate AccountExpirationDate Context
AccountLockoutTime AccountLockoutTime ContextRaw
AdvancedSearchFilter AdvancedSearchFilter ContextType
AllowReversiblePasswordEncryption AllowReversiblePasswordEncryption Description
BadLogonCount BadLogonCount DisplayName
Certificates Certificates DistinguishedName
Context Context GroupScope
ContextRaw ContextRaw Guid
ContextType ContextType IsSecurityGroup
Current DelegationPermitted Members
DelegationPermitted Description Name
Description DisplayName SamAccountName
DisplayName DistinguishedName Sid
DistinguishedName Enabled StructuralObjectClass
EmailAddress Guid UserPrincipalName
EmployeeId HomeDirectory
Enabled HomeDrive
GivenName LastBadPasswordAttempt
Guid LastLogon
HomeDirectory LastPasswordSet
HomeDrive Name
LastBadPasswordAttempt PasswordNeverExpires
LastLogon PasswordNotRequired
LastPasswordSet PermittedLogonTimes
MiddleName PermittedWorkstations
Name SamAccountName
PasswordNeverExpires ScriptPath
PasswordNotRequired ServicePrincipalNames
PermittedLogonTimes Sid
PermittedWorkstations SmartcardLogonRequired
SamAccountName StructuralObjectClass
ScriptPath UserCannotChangePassword
Sid UserPrincipalName
SmartcardLogonRequired
StructuralObjectClass
Surname
UserCannotChangePassword
UserPrincipalName
VoiceTelephoneNumber

And if you would compare that to the full attribute set of Active Directory I can safely say this is less than 10% of it, To view a full list you can visit this MSDN (http://msdn.microsoft.com/en-us/library/ms675090%28v=VS.85%29.aspx) or better yet this website (http://www.rlmueller.net/UserAttributes.htm) as it has a really good excel speadsheet that defines each all attributes in a default installation which you can download as well.

So the question is how do I expose the other attributes like UserPrincipals mobile,  UserPrincipals facsimileTelephoneNumber, GroupPrincipals info, etc.

Thanks to Principal Extensions!  With this, Principals mentioned above can all be extended to create custom objects that extend the object model.  These extended classes have now the ability to add or remove properties from the extended class as long as the properties that are added or removed are supported in the directory schema.  Now to get and set the properties of the extension class you will use the methods ExtensionGet and ExtensionSet and here
is a sample below on a Group Principal for the “wWWHomePage” attribute etc. Thanks to Principal Extensions!  With this, Principals mentioned above can all be extended to create custom objects that extend the object model.  These extended classes have now the ability to add or remove properties from the extended class as long as the properties that are added or removed are supported in the directory schema.  Now to get and set the properties of the extension class you will use the methods ExtensionGet and ExtensionSet and here is a sample below on a Group Principal for the “wWWHomePage” attribute.

[DirectoryObjectClass("group")]
[DirectoryRdnPrefix("CN")]

public class GroupPrincipalsEx : GroupPrincipal
{
    public GroupPrincipalsEx(PrincipalContext context) : base(context) { }

    public GroupPrincipalsEx(PrincipalContext context, string samAccountName)
        : base(context, samAccountName)
    {
    }

    [DirectoryProperty("wWWHomePage")]
    public string wWWHomePage
    {
        get
        {
            if (ExtensionGet("wWWHomePage").Length != 1)
                return null;

            return (string)ExtensionGet("wWWHomePage")[0];

        }
        set { this.ExtensionSet("wWWHomePage"value); }
    }
}

We call our class GroupPrincipalEx which is the extended class of GroupPrincipal which then exposes the Directory Property “wWWHomePage”.  So from the screenshot below you can see that the “wWWHomePage” now is exposed on intellisense.

You can use this in many scenarios and one of the most useful one is on searching, just imagine if you had extended your active directory schema and you want to search using that extended attribute, or even use the built in attributes but was not exposed by the base class.  I have an example to let you do that, let say you had used the wWWHomePage attribute to store the AD Group Team Site address in Sharepoint and you want to search for all groups that have the parent site “http://sharepoint.com/mydepartment/“, now here is how its done

First extend your Group Principal by using the same code above then use PrincipalSearcher to use a Query Filter based on the exposed property.

ArrayList myItems = new ArrayList();

PrincipalContext oPrincipalContext = new PrincipalContext
    (ContextType.Domain,
    sDomain,
    sDefaultOU,
    ContextOptions.SimpleBind,
    sServiceUser,
    sServicePassword);
GroupPrincipalsEx oGroups = new GroupPrincipalsEx(oPrincipalContext);
oGroups.wWWHomePage = "http://sharepoint.com/mydepartment/*";

PrincipalSearcher mySearch = new PrincipalSearcher(oGroups);
mySearch.QueryFilter = oGroups;

PrincipalSearchResult<Principal> oPrincipalSearchResult = mySearch.FindAll();

foreach (Principal oResult in oPrincipalSearchResult)
{
    myItems.Add(oResult.SamAccountName.ToString());
}

Another good example would be setting a value on that attribute on creation of the AD Object

PrincipalContext oPrincipalContext = new PrincipalContext
    (ContextType.Domain,
    sDomain,
    sDefaultOU,
    ContextOptions.SimpleBind,
    sServiceUser,
    sServicePassword);
GroupPrincipalsEx oGroups = new GroupPrincipalsEx(oPrincipalContext);

oGroups.DisplayName = "My Group";
oGroups.SamAccountName = "My Group";
oGroups.IsSecurityGroup = true;
oGroups.Description = "Automatically Created by Your Code";
oGroups.GroupScope = GroupScope.Local;
oGroups.wWWHomePage = "http://sharepoint.com/mydepartment/mygroup";
oGroups.Save();

So I guess thats it for now.

Using Google Analytics in Flash

Using Google Analytics in Flash is a very straightforward approach, I just found some of my Flash Project with one so might as well share how I made it.

What do you need?

  1. Your Flash Code
  2. Analytics Library for Flash which you can get here
  3. Definitely and Account with Google

Now once you have everything in place just add the Analytics on your library by going to File -> Import -> Import to Library.

Import To Library

Once imported you can see it on your library and use it.

GA in Library

Once its there you can now add a reference of the library in your codes as such

import com.google.analytics.API;
import com.google.analytics.AnalyticsTracker;
import com.google.analytics.GATracker;

If you want to check the version you are using add this piece ot your code

trace(API.version); 

Now for the code

var tracker:AnalyticsTracker = new GATracker( this, "XX-9999999-1", "AS3", false );

//track on click and separate in different folders
var onButtonClick:Function = function( event:Event ):void
{
 tracker.trackPageview( "/Your Folder/" );
 var request:URLRequest = new URLRequest("http://www.test.com");
 navigateToURL(request, "_self");
}

TrackBtn.addEventListener( MouseEvent.CLICK, onButtonClick );

Now lets dissect whats happening:

on the code new GATracker( this, “XX-9999999-1″, “AS3″, false );

  • The second parameter would be your Web Property ID – This is also known as the UA number of your tracking code and looks like UA-xxxxx-yy
  • The thrid parameter is the tracking mode – you have a choice of either AS3 or Bridge.  So whats the difference? Bridge Mode is used if you control both the HTML page and the Flash content (if you own the webserver you are hosting your page or at least have control on it). This mode is best if you have already implemented Google Analytics (ga.js) tracking on your website and you want to add tracking to embedded Flash content.

    AS3 Mode is used if you control the Flash ActionScript 3 code, but you do not own or at least control the webserver.  This is completely independent of the Google Analytics (ga.js) tracking and contains all the tracking functionality.  This is good to use if you want to distribute your Flash applications to different sites.

  • The fourth parameter is the debugging mode -  I guess this is self explanatory

now on the code tracker.trackPageview( “/Your Folder/” )

This is just simply incrementing the pageview count for the virtual page “/Your Folder”

So thats its really simple and straight forward

Send E-mail from Flash using .Net (in 5 easy steps)

Sending emails from Flash using .Net is really simple all you need is a Flash Interface that sends the request to an ASPX page.  Here are the things you need

1. And ASPX Page that accepts the request (in this sample lets call is EmailSender.aspx)
2. A Flash frontend for users to post their messages (in this sample lets call is EmailForm.fla)
3. And Action Script class to process the message (in this sample lets call is EmailForm.as)

Step 1 : Create EmailForm.fla and add the following objects with the following properties

Name : Type = Input Text, Name = txtName
Email Address : Type = Input Text, Name =txtEmailAddress
Subject : Type = Input Text, Name =txtSubject
Message : Type = Input Text, Name =txtMessage
Error : Type = Dynamic Text, Name =txtError
Send Email Button : Type = Button, Name =btnSendEmail

Email Form Properties

Step 2 : Create an AS file, called EmailForm.as and save it in the same directory create and add these codes

New AS

package  
{
 import flash.display.MovieClip;
 import flash.display.SimpleButton;
 import flash.events.MouseEvent;
 import flash.utils.Timer;
 import flash.events.*;
 import flash.net.URLLoader;
 import flash.net.URLVariables;
 import flash.net.URLRequest;
 import flash.net.URLRequestMethod;
 import flash.net.URLLoaderDataFormat;
 import flash.ui.ContextMenu;
 import flash.ui.ContextMenuItem;
 import flash.net.navigateToURL;

 public class EmailForm extends MovieClip
 {
 private var oTimer:Timer;
 private var bCheckStatus:Boolean=false;

 public function EmailForm()
 {
 init();
 initListener();
 }

 private function init():void
 {

 }

 private function initListener():void
 {
 btnSendEmail.addEventListener(MouseEvent.MOUSE_DOWN,controlFields);
 }

 private function controlFields(m:MouseEvent):void
 {
 if(txtName.text!=''&&txtEmailAddress.text!=''&&txtSubject.text!=''&&txtMessage.text!='')
 {
 if(CheckEmail(txtEmailAddress.text))
 {
 bCheckStatus=true;
 sendEmail();
 }
 else
 {
 bCheckStatus=false;
 txtError.text='Email Address is Invalid';
 toggleAlert();
 }
 }
 else
 {
 bCheckStatus=false;
 txtError.text='You need to fill in required fields';
 toggleAlert();
 }
 }

 private function toggleAlert():void
 {
 oTimer=new Timer(2000,1);
 oTimer.addEventListener('timer',cancel);
 oTimer.start();
 }

 private function cancel(t:TimerEvent):void
 {
 txtError.text='';
 if(bCheckStatus)
 {
 txtName.text='';
 txtEmailAddress.text='';
 txtSubject.text='';
 txtMessage.text='';
 }
 bCheckStatus=false;
 }

 private function sendEmail():void
 {
 var oEmailFields:URLVariables=new URLVariables();
 oEmailFields.sEmail=txtEmailAddress.text;
 oEmailFields.sMessage=txtMessage.text;
 oEmailFields.sSubject=txtSubject.text;
 oEmailFields.sName=txtName.text;

 var oRequest:URLRequest=new URLRequest();

 oRequest.url='EmailSender.aspx';
 oRequest.method=URLRequestMethod.POST;
 oRequest.data=oEmailFields;
 var oLoader:URLLoader=new URLLoader();
 oLoader.dataFormat=URLLoaderDataFormat.VARIABLES;
 addListeners(oLoader);
 try
 {
 loader.load(oRequest);
 }
 catch (error:Error)
 {
 trace('Page Not Found');
 }
 }

 private function addListeners(d:IEventDispatcher):void
 {
 d.addEventListener(Event.OPEN,Initialize);
 d.addEventListener(ProgressEvent.PROGRESS,inProgress);
 d.addEventListener(Event.COMPLETE,Completed);

 d.addEventListener(HTTPStatusEvent.HTTP_STATUS,HTTPStatus);
 d.addEventListener(SecurityErrorEvent.SECURITY_ERROR,SecurityError);
 d.addEventListener(IOErrorEvent.IO_ERROR,IOError);
 }

 private function Initialize(e:Event):void
 {
 txtError.text='Sending ...';
 }

 private function inProgress(e:ProgressEvent):void
 {
 txtError.text='Sending ...';
 }

 private function Completed(e:Event):void
 {
 var loader:URLLoader=URLLoader(e.target);
 var vars:URLVariables=new URLVariables(loader.data);
 if(vars.answer=='ok')
 txtError.text='Message Sent';
 else
 txtError.text='Error (Message not Sent)!!!';
 toggleAlert();
 }

 private function SecurityError(e:SecurityErrorEvent):void
 {
 txtError.text='Error (Security)!!!';
 }

 private function HTTPStatus(e:HTTPStatusEvent):void {}

 private function IOError(e:IOErrorEvent):void
 {
 txtError.text='Error (IO)!!!';
 }
 public function CheckEmail(s:String):Boolean
 {
 var x:Number;
 var y:Number=0;
 var boolTest:Boolean=new Boolean();
 for(var i:Number=0;i<s.length;i++)
 {
 if(s.charAt(i)==' ')
 {
 boolTest=false;
 break;
 }
 if(s.charAt(i)=='@')
 {
 x=i;
 y++;
 }
 else if(i>x&&s.charAt(i)=='.'&&s.charAt(s.length-1)!='.')
 {
 y++
 boolTest=true
 break;
 }
 else
 {
 if(i==s.length-1)
 {
 boolTest=false;
 break;
 }
 }
 }
 return boolTest;
 }

 }
}

Step 3 : Use that class on your EmailForm.fla

Declare Class

Step 4: Create the EmailSender.aspx file and copy these codes

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Net.Mail;
public partial class _Default : System.Web.UI.Page
{
 protected void Page_Load(object sender, EventArgs e)
 {
 if (Request.Form["sEmail"] != null)
 {
 string sPOSTEmail = Request.Form["sEmail"].ToString();
 string sPOSTMessage = Request.Form["sMessage"].ToString();
 string sPOSTSubject = Request.Form["sSubject"].ToString();
 string sPOSTName = Request.Form["sName"].ToString();

 MailMessage oMailMessage = new MailMessage();
 SmtpClient oSMTPClient = new SmtpClient();

 MailAddress oFromAddress = new MailAddress(sPOSTEmail, sPOSTName);

 string sEmailUser = "XXXXXXXXXXXXXX";
 string sEmailPassword = "XXXXXXXXXXXXXXXXX";
 string sEmailServer = "XXXXXXXXXXXXXXXXX";
 int iEmailServerSMTPPort = 25;

 oSMTPClient.Host = sEmailServer;
 oSMTPClient.Port = iEmailServerSMTPPort;

 System.Net.NetworkCredential oCredentials = new System.Net.NetworkCredential(sEmailUser, sEmailPassword);
 oSMTPClient.Credentials = oCredentials;

 oMailMessage.From = oFromAddress;
 oMailMessage.To.Add("XXXXXXXXXXXXXXXXXXXXXXX");
 oMailMessage.Subject = sPOSTSubject;

 oMailMessage.IsBodyHtml = true;
 oMailMessage.Body = sPOSTMessage;

 oSMTPClient.Send(oMailMessage);
 }

 }
}

Step 5: Youre ready to rock and roll, you can publish your site now

Follow

Get every new post delivered to your Inbox.

Join 774 other followers