CSLA .NET

Vibrant discussion about CSLA .NET and using the framework to build great business applications.

Forum has moved

New location: CSLA .NET forum


CSLA .NET Resources:
  • CSLA .NET forum
  • CSLA .NET home page
  • Issue accessing custom properties from my custom identity

    rated by 0 users
    Answered (Verified) This post has 1 verified answer | 2 Replies | 1 Follower

    Not Ranked
    3 Posts
    fujiiface posted on Fri, May 29 2015 10:38 AM

    Hello,

    Thank you in advance for any help provided here.  I read the answers on this post and still have a problem with my custom Principal/Identity implementation.

    I am using Csla 4.1.0 and seem to be having a cast issue.  This is the same scenario as the OP in the linked thread where I am trying to get to custom properties that have been implemented into my CustomIdentity class.

    When I try to cast Csla.ApplicationContext.User.Identity to CustomIdentity, I get "Unable to cast object of type 'System.Security.Principal.GenericPrincipal' to 'DMS.Library.Security.CustomIdentity.'

    Here's the code in question:

    public int UserOrgCount {

                get {

                    var identity = (CustomIdentity)Csla.ApplicationContext.User.Identity;

                    return identity.Orgs.Count();

                }

            }

    I thought I followed the ebook pretty well and in fact everything seems to be working.  The only thing I want to get access to now is the Identity properties so that I can replace individual database calls with data that's already been loaded into my custom properties.

    Let me know if there is any other information I can provide.

    For reference, here is my CustomPrincipal and CustomIdentity objects:

     

    using System;

    using System.Security.Principal;

    using Csla.Security;

    using Csla.Serialization;

     

    namespace DMS.Library.Security {

        [Serializable]

        public class CustomPrincipal : CslaPrincipal {

            private CustomPrincipal(IIdentity identity)

                : base(identity) { }

     

            /// <summary>

            /// 

            /// </summary>

            /// <param name="username">The user's username</param>

            /// <param name="password">The user's password</param>

            /// <param name="completed"></param>

            public static void BeginLogin(string username, string password, Action<Exception> completed) {

                DMS.Library.Security.CustomIdentity.GetCustomIdentity(username, password, (o, e) => {

                    if (e.Error != null)

                        Logout();

                    else

                        Csla.ApplicationContext.User = new CustomPrincipal(e.Object);

                    completed(e.Error);

                });

            }

     

    #if !SILVERLIGHT

     

            /// <summary>

            /// NOT IMPLEMENTED - Attempts to login the user with their username and password

            /// </summary>

            /// <param name="pUserName">The user's username</param>

            /// <param name="pPassword">The user's password</param>

            /// <remarks>

            /// As of 05/07/2015, this function is not used because it will create a circular execution of code.  

            /// Validation of credentials is handled within the ValidateUser function of each implemented Membership provider.

            /// Assuming the ValidateUser function returns true, the Principal's Load function is used instead

            /// </remarks>

            public static void Login(string username, string password) {

                throw new NotImplementedException("The CustomPrincipal Login method has not been implemented.  Refer to the remarks section of method's code for an explanation.");

                //var identity = DMS.Library.Security.CustomIdentity.GetCustomIdentity(username, password);

                //Csla.ApplicationContext.User = new CustomPrincipal(identity);

            }

     

            public static void Load(string username) {

                var identity = DMS.Library.Security.CustomIdentity.GetCustomIdentity(username);

                Csla.ApplicationContext.User = new CustomPrincipal(identity);

            }

     

    #endif

     

            /// <summary>

            /// Replaces the current CustomPrincipal object with a new one that has an UnauthenticatedIdentity loaded up.

            /// </summary>

            public static void Logout() {

                Csla.ApplicationContext.User = new UnauthenticatedPrincipal();

            }

        }

    }

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Csla;
    using Csla.Security;
    using Csla.Serialization;
    using DMS.Library.Admin;
    namespace DMS.Library.Security {
        [Serializable]
        public class CustomIdentity : CslaIdentityBase<CustomIdentity> {
            
            #region Properties
                public static readonly PropertyInfo<string> EmailProperty = RegisterProperty<string>(c => c.Email);
                /// <summary>
                /// The user's E-mail
                /// </summary>
                public string Email {
                    get { return GetProperty(EmailProperty); }
                    private set { LoadProperty(EmailProperty, value); }
                }
                public static readonly PropertyInfo<Guid> UserIdProperty = RegisterProperty<Guid>(p => p.UserId);
                /// <summary>
                /// The user's internal identifier
                /// </summary>
                public Guid UserId {
                    get { return GetProperty(UserIdProperty); }
                    private set { LoadProperty(UserIdProperty, value); }
                }
                public static readonly PropertyInfo<string> FirstNameProperty = RegisterProperty<string>(c => c.FirstName);
                /// <summary>
                /// The user's first name
                /// </summary>
                public string FirstName {
                    get { return GetProperty(FirstNameProperty); }
                    private set { LoadProperty(FirstNameProperty, value); }
                }
                public static readonly PropertyInfo<string> LastNameProperty = RegisterProperty<string>(c => c.LastName);
                /// <summary>
                /// The user's last name
                /// </summary>
                public string LastName {
                    get { return GetProperty(LastNameProperty); }
                    private set { LoadProperty(LastNameProperty, value); }
                }
                public static readonly PropertyInfo<string> FullNameProperty = RegisterProperty<string>(c => c.FullName);
                /// <summary>
                /// The user's full name
                /// </summary>
                public string FullName {
                    get { return GetProperty(FullNameProperty); }
                    private set { LoadProperty(FullNameProperty, value); }
                }
                public static readonly PropertyInfo<string> HomeOrgStringProperty = RegisterProperty<string>(p => p.HomeOrgString);
                /// <summary>
                /// The user's Home OrgString
                /// </summary>
                public string HomeOrgString {
                    get { return GetProperty(HomeOrgStringProperty); }
                    private set { LoadProperty(HomeOrgStringProperty, value); }
                }
                public static readonly PropertyInfo<bool> IsApprovedProperty = RegisterProperty<bool>(p => p.IsApproved);
                /// <summary>
                /// The original membership IsApproved column
                /// </summary>
                /// <remarks>
                /// Value comes from the aspnet_Membership tables
                /// </remarks>
                public bool IsApproved {
                    get { return GetProperty(IsApprovedProperty); }
                    private set { LoadProperty(IsApprovedProperty, value); }
                }
                public static readonly PropertyInfo<List<string>> OrgsProperty = RegisterProperty<List<string>>(p => p.Orgs);
                /// <summary>
                /// Returns the list of Departments that the user belongs to
                /// </summary>
                public List<string> Orgs {
                    get { return GetProperty(OrgsProperty); }
                    private set { LoadProperty(OrgsProperty, value); }
                }
                public static readonly PropertyInfo<string> AppNameProperty = RegisterProperty<string>(p => p.AppName);
                public string AppName {
                    get { return "AppName"; }
                }
        #endregion
            public static void GetCustomIdentity(string username, string password, EventHandler<DataPortalResult<CustomIdentity>> callback) {
                DataPortal.BeginFetch<CustomIdentity>(new UsernameCriteria(username, password), callback);
            }
    #if !SILVERLIGHT
            /// <summary>
            /// Gets the user after authenticating them with their username and password
            /// </summary>
            /// <param name="pUserName">The user's employee number</param>
            /// <param name="pPassword">The user's password</param>
            /// <returns></returns>
            public static CustomIdentity GetCustomIdentity(string username, string password) {
                return DataPortal.Fetch<CustomIdentity>(new UsernameCriteria(username, password));
            }
            
            private void DataPortal_Fetch(UsernameCriteria criteria) {
                AuthenticationType = "Custom";
                using (var mgr = DataAccess.DalFactory.GetManager()) {
                    var dal = mgr.GetProvider<DataAccess.IIdentityDal>();
                    if (dal.VerifyUser(criteria.Username, criteria.Password))
                        LoadUserData(criteria.Username, dal);
                }
            }
            /// <summary>
            /// Gets the user based on their username
            /// </summary>
            /// <param name="pUserName">The user's employee number</param>
            /// <returns></returns>
            internal static CustomIdentity GetCustomIdentity(string username) {
                return DataPortal.Fetch<CustomIdentity>(username);
            }
            private void DataPortal_Fetch(string username) {
                AuthenticationType = "Custom";
                using (var mgr = DataAccess.DalFactory.GetManager()) {
                    var dal = mgr.GetProvider<DataAccess.IIdentityDal>();
                    LoadUserData(username, dal);
                }
            }
            /// <summary>
            /// Uses the DataAccess DalFactory to dynamically load the required membership provider
            /// </summary>
            /// <param name="pUserName">The user's employee number</param>
            /// <param name="dal">The Identity's data access object</param>
            private void LoadUserData(string pUserName, DataAccess.IIdentityDal dal) {
                var userData = dal.GetUser(pUserName);
                //Step 1 authentication loading - If the user exists in the membership tables, the user is temporarily authenticated
                this.IsAuthenticated = (userData != null);
                
                if (this.IsAuthenticated) {
                    this.Name = userData.UserName;
                    this.FirstName = userData.FirstName;
                    this.LastName = userData.LastName;
                    this.FullName = string.Format("{0} {1}", userData.FirstName, userData.LastName);
                    this.Email = userData.Email;
                    this.UserId = userData.UserId;
                    this.HomeOrgString = userData.HomeOrgString;
                    this.IsApproved = userData.IsApproved;
                    //Step 2 authentication loading - Update the Identity's IsAuthenticated property based on the membership table
                    this.IsAuthenticated = this.IsApproved;
                    //Initialize the MobileList property and then load up the roles once we've gotten them
                    this.Roles = new Csla.Core.MobileList<string>();
                    string[] _roles = UserRole.GetRolesForUser(pUserName, string.Empty, true);
                    foreach (var role in _roles) {
                        this.Roles.Add(role);
                    }
                    //Load up the Orgs the user belongs to
                    this.Orgs = UserOrg.GetOrgsForUser(pUserName, true, false).ToList();
                    //Step 3 authentication loading - Check to make sure that even if the user is authenticated with AD and exists in the membership table, the user has at least 1 Org and Role
                    if (!(this.Orgs.Count() > 0 && this.Roles.Count() > 0)) {
                        this.IsAuthenticated = false;
                        throw new InvalidOperationException(string.Format("User {0} does not have access to any departments and/or roles.\n    Org Count: {1};\n    Role Count: {2}", this.Name, this.Orgs.Count(), this.Roles.Count()));
                    }
                }
            }
    #endif
        }
    }

    Answered (Verified) Verified Answer

    Top 10 Contributor
    2,279 Posts
    Verified by fujiiface

    Hi,

    The issue here is that you have lost your custom principal object and now has the default GenericPrincipal which is a built-in type in .NET.

    This could be caused by some configuration error (AuthenticationType) or that your code has used async methods that does not transfer the "current" principal to the executing (background) thread. 

    CSLA contains a custom BackgroundWorker and CslaTaskScheduler that will make sure to transfer the principal and ApplicationContext to background thread. 

    Jonny Bekkum, Norway CslaContrib Coordinator

    All Replies

    Top 10 Contributor
    2,279 Posts
    Verified by fujiiface

    Hi,

    The issue here is that you have lost your custom principal object and now has the default GenericPrincipal which is a built-in type in .NET.

    This could be caused by some configuration error (AuthenticationType) or that your code has used async methods that does not transfer the "current" principal to the executing (background) thread. 

    CSLA contains a custom BackgroundWorker and CslaTaskScheduler that will make sure to transfer the principal and ApplicationContext to background thread. 

    Jonny Bekkum, Norway CslaContrib Coordinator

    Not Ranked
    3 Posts

    Hi Jonny,

    Thank you for your response and all of your support with CSLA.  Personally, I really appreciate it.

    Regarding my issue, it looks like the problem has been resolved thanks to some of your suggestions.  Essentially, I did the following:

    1. Reverted my CustomPrincipal constructor back to accept a CustomIdentity parameter (instead of IIdentity)
    2. Removed my AuthorizeDataPortal settings from Web.config
    3. Confirmed that CslaAuthentication was set to "Custom"

    To my knowledge, we are not running anything async.  Also, it appears the AuthorizeDataPortal configuration I had in place was not set up correctly.  As I recall (from when I would debug) within the AuthorizeDataPortal's Authorize method, I could never get to the CustomPrincipal.Load method.  It would always load an UnauthenticatedPrincipal.  Once I removed the concept of the AuthorizeDataPortal to avoid passing the Principal object over the wire, I was able to avoid "losing" my Csla.ApplicationContext.User.

     

    As of right now, I believe this was the configuration error you were referring to in your previous post.  If anything comes up, I will follow up.  Thank you again for all of your support!

    Page 1 of 1 (3 items) | RSS

    Copyright (c) 2006-2014 Marimer LLC. All rights reserved.
    Email admin@lhotka.net for support.
    Powered by Community Server (Non-Commercial Edition), by Telligent Systems