An Avanade Blogging Community

Welcome to An Avanade Blogging Community Sign in | Join | Help
in Search

Johan's Avanade Blog

  • Forms Authentication with Silverlight, ADAM, and AzMan

    In this post, I describe how to implement Authentication and Authorization for Silverlight using AD LDS (previously known called ADAM), and the Authorization Manager (also known as AzMan).

    Here is the list of steps involved in this implementation:

    1. Setup AS LDS
    2. Setup AzMan on AD LDS
    3. Implement a custom RoleProvider for AzMan
    4. Implement a custom Principal
    5. Implement a custom PrincipalPermission and PrincipalPermissionAttribute
    6. Implement a custom AuthorizationPolicy
    7. Implement a custom WCF Authorization Service
    8. Setup WCF Authentication, Role and Authorization Services
    9. Setup SSL
    10. Implement a custom Silverlight Principal and Identity
    11. Implement a workaround for Silverlight-enabled WCF Service Exception Handling

    Here is why I chose this implementation:

    1. I am using Silverlight 2 with VS2008 SP1 on WS2008,
    2. I am using Forms Authentication without Windows Credentials (for Internet users),
    3. I want to have a RBAC allowing me to define Operations (no Roles in code),
    4. I want to have the flexibility to use ADAM, and the eventual ability to move to Federated Services,
    5. Silverlight only works with basicHttpBinding (Geneva and Federated Serviced use wsFederationHttpBinding),
    6. AzMan only works with Windows Authentication out-of-the-box.

    Setup AD LDS

    The first step consists of installing Active Directory Lightweight Directory Services (AD LDS), previously called Active Directory Application Mode (ADAM). AD LDS is now directly included in the Roles of Windows Server 2008.

    Since, this installation is already well described in the following link, I will only add a couple instructions to this for the purpose of this post.

    The following LDF files should be included in the installation:

    • MS-AzMan.LDF
    • MS-InetOrgPerson.LDF
    • MS-User.LDF

    Once the installation completed, add the "NETWORK SERVICE" account to the "Administrators" roles:

    1. Open ADSI Edit
    2. Connect to the newly created partition ("Select a well known Naming Context")
    3. Expand Tree View up to "CN=Roles"
    4. Open "CN=Administrators" Properties
    5. Edit "member" property
    6. Add the "NETWORK SERVICE" Windows Account

    Furthermore, if you want to setup SSL, you can simply create a "Self-Signed Certificate" for development purpose in IIS7 (Server Certificates).

    Links:

    Setup AzMan

    This second step consists of creating an Authorization Store in AD LDS for the Authorization Manager.

    • Run "azman.msc"
    • Right click "Authorization Manager"
    • Select "New Authorization Store":
      • Select ADAM
      • Schema version 2
      • Store name: msldap://localhost:389/CN=[user store name], [your AD LDS partition]

    (*) Where [user store name] such as: MyUserStore

    • Right click the new store (MyUserStore)
    • Select "New Application" and create a new application (MyApplication)
    • Open the new application Properties
    • Open the Security tab and add the "NETWORK SERVICE" account to the Administrator role

    From now, you can define your Operations, Tasks, and Roles.

    In the following link, MSDN clearly define how to use the Authorization Manager with ASP.NET. However this implementation only uses ADAM as a repository. The users are authenticated with Windows Accounts!

    Links:

    Implement a custom RoleProvider for AzMan

    At this stage we should have AD LDS and AzMan setup, however as indicated in the previous post, AzMan only works with Windows Authentication by default. So, we need to implement a custom RoleProvider to use it with Internet users authenticated by AD LDS.

    I have added 2 new methods to MyRoleProvider:

    • public string[] GetOperationsForUser(string userName)
    • public bool CanUserAccessOperation(string userName, string operationName)

    Using the following links, and the Reflector on the AuthorizationStoreRoleProvider class, you can easily create your own provider.

    My 2 cents on this:

    • Check-out the CheckParameter() and CheckArrayParameter() methods in the AuthorizationStoreRoleProvider
    • Don't forget to dispose all COM objects that have been opened.

    For this second point, I implemented my own internal AzManStore type as follow:

    internal class AzManStore : IDisposable

    {

    public AzAuthorizationStore Store { get; private set; }

    public IAzApplication Application { get; private set; }

    public AzManStore(string applicationName, string connectionString)

    {

    if (string.IsNullOrEmpty(applicationName)) throw new AzManProviderException("ApplicationNameNotSpecified");

    try

    {

    this.Store = new AzAuthorizationStore();

    this.Store.Initialize(0, connectionString, null);

    this.Application = this.Store.OpenApplication(applicationName, null);

    }

    catch (COMException ex)

    {

    throw new AzManProviderException("InitializeFailed", ex);

    }

    catch (Exception ex)

    {

    throw new AzManProviderException("InvalidConnectionString", ex);

    }

    }

    public void Dispose()

    {

    if (this.Application == null) return;

    Marshal.FinalReleaseComObject(this.Application);

    Marshal.FinalReleaseComObject(this.Store);

    this.Application = null;

    this.Store = null;

    }

    }

    Then, I can open my AzMan application as follow:

    using (var store = new AzManStore(this.ApplicationName, this.ConnectionString))

    {

    var roles = store.Application.Roles;

    // do something

    }

    Once this new RoleProvider created, you can sign you DLL, put it in the GAC, add the new Provider to the trusted list, and setup your Web Application (Silverlight Web Application Host) to enable Authentication and Authorization.

    To add the new provider to the trusted list, open and edit the Administration.config file located in:

    C:\Windows\System32\inetsrv\config

    Here are the lines to add to your web.config, however you should be able to setup everything from IIS7:

    < name="MyMembershipProvider">

    type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

    connectionStringName="MyAdamConnectionString"

    connectionProtection="Secure"

    applicationName="MyApplication"

    enableSearchMethods="true" />

    < name="MyRoleProvider">

    type="MyLibrary.MyRoleProvider, MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1111111111"

    connectionStringName="MyAzManConnectionString"

    applicationName="MyApplication" />

    At this point, you should be able to setup your users and roles directly from IIS7, or from the ASP.NET Configuration site!

    Links:

    Implement a custom Principal

    In this step, I propose to create a custom Principal that will be used on the Server. This Principal will allow to directly access the Operations accessible by the Identity.

    public class MyPrincipal : IPrincipal

    {

    private IIdentity _identity = null;

    private string[] _operations = null;

    public IIdentity Identity { get { return this._identity; } }

    public MyPrincipal(IIdentity identity, string[] operations)

    {

    this._identity = identity;

    this._operations = operations;

    }

    public bool IsInRole(string role)

    {

    throw new NotImplementedException();

    }

    public bool HasRequiredOperations(string[] requiredOperations)

    {

    if (requiredOperations == null) return true;

    if (this._operations == null) return false;

    var hasOperations = true;

    foreach (var operation in requiredOperations)

    {

    if (this._operations.Contains(operation) == false)

    {

    hasOperations = false;

    break;

    }

    }

    return hasOperations;

    }

    }

    Links:

    Implement a custom PrincipalPermission and PrincipalPermissionAttribute

    Now that our Principal is created, we can create a custom PrincipalPermission and its PrincipalPermissionAttribute. The following link clearly explains how to implement these classes for a claim-based scenario. So, instead of a CheckClaims() method, you can write a CheckOperations() method and invoke the principal.HasRequiredOperation() method.

    As described in the link, this allows the use of declarative and imperative security checks as follow:

    [MyPrincipalPermission(SecurityAction.Demand, Operation = "MyOperation")]

    public void MyOperation()

    {

    // do something

    }

    Or

    public void MyOperation()

    {

    var permission = new MyPrincipalPermission("MyOperation");

    Permission.Demand();

    // do something

    }

    I particularly like this implementation, because it allows decoupling the code from the RBAC used, and the type of authentication. In this scenario I am using AzMan with a "standard Role-Based" approach, but it should not be too hard to move to the "Claim-Based" model if needed.

    Links:

    Implement a custom AuthorizationPolicy

    This step consists of creating a custom AuthorizationPolicy that will tie the things together! The AuthorizationPolicy will allow to initialize the CurrentPrincipal of the current Thread whenever a WCF Service is invoked.

    public class MyAuthorizationPolicy : IAuthorizationPolicy

    {

    private Guid _id = Guid.NewGuid();

    public string Id { get { return this._id.ToString(); } }

    public ClaimSet Issuer { get { return ClaimSet.System; } }

    public bool Evaluate(EvaluationContext evaluationContext, ref object state)

    {

    IIdentity identity = this.GetClientIdentity(evaluationContext);

    var provider = (MyRoleProvider)Roles.Provider;

    var operations = provider.GetOperationsForUser(identity.Name);

    evaluationContext.Properties["Principal"] = new MyPrincipal(identity, operations);

    return true;

    }

    private IIdentity GetClientIdentity(EvaluationContext evaluationContext)

    {

    var identity = HttpContext.Current.User.Identity;

    if (identity == null)

    {

    throw new SecurityException("NoIdentityFound");

    }

    return identity;

    }

    }

    You can notice in this code the reuse of MyRoleProvider created in step# 3, and MyPrincipal created in step# 4.

    Then, the following lines should be added to the behavior of your WCF Service (or Silverlight-enabled WCF Service):

    Links:

    Implement a custom WCF Authorization Service

    The last step left on the server side is to implement a new WCF Service to expose the following methods:

    • public string[] GetOperationsForCurrentUser()
    • public bool CanCurrentUserAccessOperation(string operation)

    These 2 methods will allow the Silverlight application to retrieve the Operations accessible by a user, and then initialize the Silverlight Identity and Principal context.

    Here again, you can use the Reflector on the System.Web.ApplicationServices.RoleService class, and rename the Role methods by the Operation methods...

    Setup WCF Authentication, Role and Authorization Services

    Now that all Authentication, Role and Authorization Services are ready to be used, you can set them up for your Silverlight application.

    Links:

    Note: It is highly recommended to secure these services with SSL.

    Setup SSL

    The following link describes how to enable SSL on your application. This is highly recommended to secure your application.

    Links:

    Implement a custom Silverlight Principal and Identity

    We are almost there! At this stage, everything is ready on the Server, however on the client side there is no Identity and Principal implemented in Silverlight. Fortunately, the IIdentity and IPrincipal interfaces are exposed. So, you can create your custom classes.

    For this part, I have decided to go for a solution with a SilverlightIdentity and a SilverlightPrincipal as proposed in this link. I have added a CanAccessOperation(string operation) method to allow the Silverlight application to check if a user has access to an Operation.

    The SilverlightIdentity and SilverlightPrincipal classes are initialized using the Authentication, Role, and Authorization WCF Services previously added to the Web Application (host) for this purpose. So, they need to be referenced in the Silverlight project.

    Links:

    Implement a workaround for Silverlight-enabled WCF Service Exception Handling

    WCF comes with the FaultException class to provide a clean solution to exchange error details between Client and Server. However, up to now Silverlight does not work properly with this solution. An error 404 Not Found is returned!

    You will find several solutions to this (refer to links), and here is mine. Add the following classes to the Web Application exposing your Silverlight-enabled WCF Services:

    [DataContract]

    [KnownType(typeof(MyResult))]

    public class ResponseData

    {

    [DataMember]public Error Error { get; private set; }

    [DataMember]public object Data { get; private set; }

    public ResponseData(Exception ex)

    {

    this.Error = new Error()

    {

    Type = ex.GetType().Name,

    Message = ex.Message

    };

    }

    public ResponseData(object data)

    {

    this.Data = data;

    }

    }

    [DataContract]

    public class Error

    {

    [DataMember]public string Type { get; set; }

    [DataMember]public string Message { get; set; }

    }

    Add the type of your result to the ResponseData using the KnownType attribute. This allows the Service Utility to generate your classes with the proxy.

    Then, use it to return your result to Silverlight:

    [OperationContract]

    public ResponseData MyOperation()

    {

    try

    {

    var result = new MyResult("Hello world!");

    return new ResponseData(result);

    }

    catch (Exception ex)

    {

    return new ResponseData(ex);

    }

    }

    Links:

    Wrap-Up

    Silverlight can be setup to use Forms Authentication and WCF Authentication & Role Services.  In this post, I proposed an implementation combining AD LDS and the Authorization Manager.  This allows Web Applications dedicated to Internet Users to benefit from the standard Administration tools (ADAM and AzMan), and also decouple the Application from these Administration tools.

  • ADO.NET Data Services: how to invoke a WebGet service operation from a WebClient

    I have been working lately with ADO.NET Data Services, and I found several tutorials on how to create your first services and service operations.  But, then once I wanted to consume my service operations (WebGet), I was in the clouds...

    So, here are my 2 cents...

    First, here is the context of the service operation on the server:

    public class MyService : DataService<MyEntities>
        {
            // This method is called only once to initialize service-wide policies.
            public static void InitializeService(IDataServiceConfiguration config)
            {
                config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
            }

            [WebGet]
            public IQueryable<Program> GetPrograms(int code, DateTime startDate, DateTime endDate)
            {
                return from program in this.CurrentDataSource.Program
                       where program.Code == code
                          && program.Date >= startDate.Date
                          && program.Date < endDate.Date
                       select program;
            }

    }

    Then, on the client I can invoke it as follow:

    public IList<Program> Retrieve(int code, DateTime startDate, DateTime endDate)
    {           
            var context = new MyService.MyEntities(<service url>);
            context.MergeOption = MergeOption.AppendOnly;
            var list = context.CreateQuery<Program>("GetPrograms")
                .AddQueryOption("code", code)
                .AddQueryOption("startDate", string.Format("datetime'{0:yyyy-MM-ddTHH:mm:ss}'", startDate))
                .AddQueryOption("endDate", string.Format("datetime'{0:yyyy-MM-ddTHH:mm:ss}'", endDate));

            return list.ToList();
    }

    Note that the parameters must be added inline to the CreateQuery method, and I had to use specific DateTime format for my parameters.

    Links

    Create Data-Centric Web Applications With Silverlight 2

    ADO.Net Data Services Part 1 - Building a Simple Web Data Service

    ADO.Net Data Services Part 2 - Using Service Operations with WebGet

    Service Operations in ADO.NET Data Services

  • Silverlight Debugger suddenly stopped working!

    I have been working on a Silverlight app (using VS2008 and Blend June Preview), and I first noticed that, sometimes, when I ran my app I did not have the latest code, so I stopped, rebuilt, and restarted the app and it usually worked.

    But this week, I have been blocked for an hour when my Debugger suddenly stopped working.  I thought the changes I made introduced strange behaviors causing the ASP.NET Development Server to stop.  Then, I realized that for some reasons my latest code was not cached anymore by IE!

    So, if your Silverlight app does not appear with your latest changes, or if your Debugger suddenly stops working, clear the "Temporary Internet Files" in IE ("Tools\Delete Browsing History..." menu).

    UPDATE

    Since this also happens when I update the structure of my solution (moving project in sub-folders, etc.), I also need to check the Properties of my Web Application.  The "Silverlight" CheckBox should be checked under Web / Debuggers.

    Links:

    Silverlight.net Forum - Debugger stopped working

    Silverlight.net Forum - aspx file not displaying the last .xap build

  • showModalDialog and PostBack

    Working on a MOSS component, I implemented a Modal Popup form including a SPGridView with Sorting an Filtering features.  unfortunately, I spent "huge" amount of time figuring-out how to make the grid working properly with its PostBack events.  The main issue was every javascript call resulted in the opening of a new form...

    I found a bunch of posts regarding this, and the main advice was to avoid using the showModalDialog for non-static content.  Just use the window.open()...

    I finally found this post.  The result was to add the following line to the <head> tag of my popup form:

    <base target="_self"/>

    With this all my javascript code and PostBack events work like a charm!

  • MenuItemTemplate and ClientOnClickUsingPostBackEvent

    I spend a fair amount of time before figuring out how to implement a Menu Item that would trigger a Post Back Event.  So, thanks to Powlo's blog, SharePoint Users Group and the MSDN forum.

    My overall goal was to implement a "Delete" MenuItemTemplate that triggers a Event on the server.  Here are the steps for my implementation:

    1. Implement the IPostBackEventHandler and its RaisePostBackEvent method.
    2. Add the MenuItemTemplate as follow in the aspx file:

    <SharePoint:MenuItemTemplate ID="DeletePortfolioMenuItem"
                                         runat="server"
                                         Text="Delete"
                                         Description="Delete this Portfolio"
                                         ImageUrl="/_layouts/images/delitem.gif"
                                         Sequence="2"
                                         ClientOnClickPostBackConfirmation="Are you sure you want to delete this item?"
                                         ClientOnClickUsingPostBackEvent="__page,%Name%" />

    Or in the codebehind by setting the ClientOnClickPostBackConfirmation and ClientOnClickUsingPostBackEvent in the CreateChildControls method:

    protected override void CreateChildControls()
    {
        base.CreateChildControls();

        DeletePortfolioMenuItem.ClientOnClickPostBackConfirmation = "Are you sure you want to delete this item?";
        DeletePortfolioMenuItem.ClientOnClickUsingPostBackEvent = "__page,%Name%";
    }

    The %Name% parameter is used here as the TokenNameAndValueFields in the SPMenuField of the SPGridView.  Its value is passed to the Event Argument of the RaisePostBackEvent method.

    Note: The functionality can also be implemented with the ClientOnClickScript as follow:

    DeletePortfolioMenuItem.ClientOnClickScript = "if (confirm('Are you sure you want to delete this item?')) __doPostBack('" + this.UniqueID + "','%Name%')";

  • New MIME to host Silverlight 2 Beta 1

    Recently, I upgraded my personal blog that contains a little Silverlight animation from Silverlight Alpha 1.1 to Silverlight 2 Beta 1.  So, I basically recreated my Silverlight project to have a look at the new features.

    When came the moment to deploy this new version to my host, I had a surprise: the animation was totally blank! :(

    So, what?

    Silverlight 2 now packs all required resources into a .XAP file (this is already the case for .XPS files).  So, this new type has to be added to IIS as application/x-silverlight-app.

    Links:

    How to configure IIS 6.0 to host Silverlight 2

    Using Silverlight 2 on a production Web Server

    Technorati Tags: ,,,
  • SharePoint 2007 Workflow with InfoPath

    This subject has already been described and detailed for a long time, so, the intent of this post is not to add another implementation on the subject, but gives a checklist and shares few issues faced.

    This checklist has been done using the following tools:

    • MOSS 2007
    • Visual Studio 2005
    • InfoPath 2007

    Setup the Working Environment

    1. Open Visual Studio 2005 and create a SharePoint Server Sequential Workflow project.

    InfoPath Form

    2. Open InfoPath 2007 and create a new Design Form Template.

    3. Add controls and fields to the InfoPath form.

    4. Check the Compatibility of the form.

    5. Change the Security to "Domain".

    6. Save the InfoPath file and Publish the form to "DevelopmentFiles/FeatureFiles" folder in the Visual Studio 2005 project that was created in step# 1.

    7. Save the InfoPath form as Source File.

    Visual Studio Project

    8. Execute the xsd.exe tool on the newly created myschema.xsd source file:

    Example: xsd.exe myschema.xsd /c

    This will generate the class corresponding to the fields used in the InfoPath form.

    9. Copy and rename the myschema.cs to the Visual Studio project.

    10. Update feature.xml and workflow.xml using Snippets.

    11. Code your workflow activities.

    Deployment

    12. Open the Project Properties, click on Build Events and change "NODEPLOY" to "DEPLOY" in Post-build event command line

    13. Sign the assembly and install it to the GAC.

    From this point you should be able to access your Workflow from "Site Settings / Galleries / Workflows", and attach it to any List.

    If you need to access the Workflow from the SharePoint Designer few more steps are needed.

    Setup the Workflow for SharePoint Designer

    14. Add an action entry in the WSS.ACTIONS file located in the following path:

    C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\1033\Workflow 

    15. Add an authorizedType entry to the web.config file of the Web Application.

    Now, the Workflow action is accessible from the New Workflow menu option.

     

    Issues Faced

    • The main issue faced is whenever you have an error in your InfoPath form and you get "The form has been closed" in SharePoint, or whenever you want to update the InfoPath form, the easiest is to delete the InfoPath Template and re-create a new one.

    Links:

    SharePoint 2007 Workflow with Visual Studio 2005 + InfoPath 2007

    Deploying a custom MOSS 2007 workflow

  • Windows Vista SP1 and Windows Server 2008 set for Launch!

    Both versions will be available mid-March.

    After a little check on MSDN Subscriber, WS2008 x64 is already available for download!  The other versions are coming.  The good news is it already contains Hyper-V (other versions will be available Without Hyper-V).  After using the RC1 on my laptop for a couple of weeks, I can say that my short experience is really positive: it's a lot faster on my configuration with Hyper-V than my previous one with Vista x86 and Virtual PC, and that it has been really stable until now.  So, I am pretty eager to install the final version.

    For more details about the install, follow Bryant's post about switching his laptop to Windows Server 2008.

    Links:

    Windows Vista SP1 Set For Launch, Microsoft Says

    Windows Vista SP1 et Windows Server 2008 finalisés

This Blog

Post Calendar

<March 2010>
SuMoTuWeThFrSa
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

Syndication