Print Page | Close Window

How to use LoginAsync when using a custom LoginMangarer?

Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce 2009
Forum Discription: For .NET 3.5
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=1448
Printed Date: 16-Apr-2024 at 2:53am


Topic: How to use LoginAsync when using a custom LoginMangarer?
Posted By: tj62
Subject: How to use LoginAsync when using a custom LoginMangarer?
Date Posted: 31-Aug-2009 at 9:26am
Hi,
I'm trying to implement my own LoginManager. To do that I started from the security sample (found at "C:\Program Files\IdeaBlade DevForce\Learning Resources\090_Security\Code Samples\300 Common\CodeCS\Security_ConsoleUI").
I managed to get my own LogingManager working very will following the same design strategy.
 
However how do I use that sample when replacing the console client for a SilverLight client?
 
In the console client I do this to login:

C2NetDomainModel.LoginClientCore.Instance.Login( userName , password , domain );

This call calls my custom  LoginManager normaly (that inheritates from IEntityLoginManager) entering the method:
  public IPrincipal Login( ILoginCredential pCredential , EntityManager pManager )
as expected
 
However in my silverlight client when calling:
   var cred = new FormsAuthenticationLoginCredential("userA","passA","",true);
   WriteMessage("Logging in ...");
   _mgr.LoginAsync(cred, LoggedIn, null);
 
I get this error:
"LoginManager is required but failed to find one."
What do I have to do extra to have LoginAsync() acess my LoginManager?
 



Replies:
Posted By: kimj
Date Posted: 31-Aug-2009 at 6:01pm
DevForce probes for a custom IEntityLoginManager based on the <probeAssemblyNames> specified at the root of the ideablade.configuration - not for a specific EdmKey.  The debuglog on the server will include a trace message when your custom implementation is found and loaded.
 
 


Posted By: tj62
Date Posted: 01-Sep-2009 at 3:25am
Of course I have set the <probeAssemblyNames>, however I still have the problem that it is not activated when calling LoginAsync from my silverlight client.

Here is my app.config file in the Domain Model:
 
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="ideablade.configuration" type="IdeaBlade.Core.Configuration.IdeaBladeSection, IdeaBlade.Core, Version=5.2.1.0, Culture=neutral, PublicKeyToken=287b5094865421c0" />
  </configSections>
  <ideablade.configuration version="5.00" updateFromDomainModelConfig="Ask">
    <probeAssemblyNames>
      <probeAssemblyName name="C2NetServer" />
    </probeAssemblyNames>
    <logging logFile="DebugLog.xml" />
    <objectServer isDistributed="false" remoteBaseURL="
http://localhost - http://localhost " serverPort="9009" serviceName="EntityService" />
    <edmKeys>
      <edmKey name="Default" connection="metadata=res://ServerModelC2Net/ServerModelC2Net.csdl|res://ServerModelC2Net/ServerModelC2Net.ssdl|res://ServerModelC2Net/ServerModelC2Net.msl;provider=IBM.Data.DB2;provider connection string=&quot;Database=c2net;User ID=userA;Password=passwordA;Server=192.168.1.71:9091;Persist Security Info=True&quot;" containerName="ServerModelC2Net.C2NetEntities">
        <probeAssemblyNames>
          <probeAssemblyName name="C2NetDomainModel" />
          <probeAssemblyName name="ServerModelC2Net" />
        </probeAssemblyNames>
      </edmKey>
    </edmKeys>
  </ideablade.configuration>
</configuration>
 
Note the red lines.  I have also checked if the settings is in web.config of the web-broject, and it is. It is also in the SilverLight application project.
Please not my previous posting: It works perfectly when using a console client, but not when calling LoginAsync() from my Silverlight client. 
So what more is needed?


Posted By: tj62
Date Posted: 01-Sep-2009 at 5:38am
Kimj,
I found the problem. the web project has to have a reference to the LoginManager assembly. After that  my LoginManager is created and it's Login method is called. However when returned to the client I get this message in args.Error.Message (silverlight application login user callback method):
 
The Login could not be completed because the server did not respond.  Check that the server is available and contains a clientApplicationType='Silverlight' configuration setting.
 
The clientApplicationType='Silverlight' is set in Web.config. So what is cusing that there is no response.

Here is my simple LoginManager.Login method for testing purpose only (it only returns a valid IPrincipal, no validation done):
 

  //[System.Diagnostics.DebuggerHidden()] // Let client catch exceptions
  public IPrincipal Login( ILoginCredential pCredential , EntityManager pManager )
  {
   EnterLogin(); // writes to the debug log.
   if( pCredential == null )
   {
    throw new LoginException( LoginExceptionType.NoCredentials , null , null );
   }
   // Create a test identity
   IIdentity identity = new C2NetDomainModel.AppIdentity( pCredential.UserName , 3 , "UserPassword" );
   // Set some roles just for testing
   string[] roles = new string[] { "Admin" , "User" , "SalesRep" , "SalesMgr" , "Guest" };
   IPrincipal principal = new GenericPrincipal( identity , roles );
   ExitingSuccessfully(); // writes to the debug log.
   return principal;
  }
I noticed that the Login method of my login manager is called twice as a result of one LoginAsync call from the silver light client application. I'm a little stuck in the fog here. The documentation is rather confuising and missing implementation example for silver light.
Can you help me bring this to the end?


Posted By: kimj
Date Posted: 01-Sep-2009 at 8:22am
I should have added that the assembly to be probed also has to be findable - i.e. in the bin folder - which is why adding a reference from your web project worked.
 
The reason you see two Login calls is because DevForce retry logic kicked in after the first failed login and tried one more time.
 
I'd guess the problem is in the serializability of the return type.  DevForce Silverlight uses the DataContractSerializer, which needs to be told about "known types".  DevForce defines the GenericPrincipal as a KnownType, but you should also either derive AppIdentity from the IdeaBlade.EntityModel.IKnownType marker interface or decorate it with the IdeaBlade.EntityModel.DiscoverableType(DiscoverableTypeMode.KnownType) attribute.
 
Edit - I wasn't familiar with the tutorial and just took a look - the AppIdentity class is not usable in Silverlight right now.  The Serializable attribute isn't supported in SL, so the class either needs to be left undecorated or to use the DataContract/DataMember attributes.  Unfortunately without public setters the class still won't serialize. 
 
For your testing purposes, why not just return the GenericIdentity?  Our Documentation Manager will make a new working AppIdentity available soon.


Posted By: tj62
Date Posted: 01-Sep-2009 at 9:28am
Kimj, sorry for becoming a bit negative, but I se on the forums that there is a lot of frusturation regarding this LoginManager stuff.
The documentation is horrible and there is a huge lack of samples.
So many of us are trying to do this in the fog by trial & error and not full understanding of how things work.

Anyway I changed:
 
public class AppIdentity : IIdentity
  to
public class AppIdentity : IIdentity , IdeaBlade.EntityModel.IKnownType
 
Now when I run this the LoginManager.Login method is called only once but this error message is returned to the client:
 
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://ideablade.com/EntityModel:LoginResult - http://ideablade.com/EntityModel:LoginResult . The InnerException message was 'Element 'http://schemas.datacontract.org/2004/07/System.Security.Principal:m_identity' contains data of the 'http://schemas.datacontract.org/2004/07/C2NetDomainModel:AppIdentity' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'AppIdentity' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'.  Please see InnerException for more details.
 
Am I doing the right thing? What exactly do you mean with "decorate" in your reply.


Posted By: kimj
Date Posted: 01-Sep-2009 at 9:39am

I edited my response just before you posted.  The AppIdentity class will not work right now, but if you use a GenericIdentity you should be OK.  DevForce intrinsically supports the GenericPrincipal and GenericIdentity types (as well as the UserBase class which combines IIdentity and IPrincipal implementations) so you don't need to do anything special.

A Silverlight application has to be able to send and receive data between the client application and the web server, so that data needs to be serializable.  The DataContractSerializer used by the EntityServer (which is the default for any WCF service) is quite picky about understanding all the types involved.  When you create a custom type - such as AppIdentity here - and want to use it on both sides of the application, then the type must be serializable and often must be made "known".   DevForce allows you to make a type known via either the attribute or interface mentioned earlier.
 
We'll look at beefing up our documentation and samples.
 


Posted By: tj62
Date Posted: 01-Sep-2009 at 9:58am
Ok I changed it to use GeneralIdentity instead of AppIdentity and it worked. However I would like to use custom identity as the only properties of GeneralIdentity are Name and IdentityType, but I would like to store the User ID to (an integer). Wil custom identites be supported in future release?

But probably I can bybass that when I understand this messy thing better.
 
Another thing I noticed in your reply is the discussion about UserBase class. This class does not seem to be recognizable by Visual Studio 2008 and although I have googled the world around I do not find any usable information about it.  It is mentioned in the DevForce User Guide but not how to use it, what assembly it is in and so on.
 
Can you tell me how to use it?


Posted By: kimj
Date Posted: 01-Sep-2009 at 10:32am
Custom identities are supported right now, you just have to jump through the hoops I've mentioned above to get it working.  Here's one -
 

[System.Runtime.Serialization.DataContract]
public class AppIdentity2 : GenericIdentity, IdeaBlade.EntityModel.IKnownType {
   public AppIdentity2(long userId, string userName) : base(userName) {
     UserId = userId;
   }
   [System.Runtime.Serialization.DataMember]
   public long UserId {
      get;
      set;
   }
}
 
IdeaBlade.EntityModel.UserBase is defined in the IdeaBlade.EntityModel.Web and IdeaBlade.EntityModel.SL assemblies.  It uses the UserIdentity class (defined in the same namespace and assemblies as UserBase) for its IIdentity implementation, so would not give you the integer UserID you need.  You can still try it out, though, just add a reference to the IdeaBlade.EntityModel.Web assembly from your assembly containing the LoginManager. 
 
We'll get a version of AppIdentity which can be used in DevForce Silverlight available as soon as we can.


Posted By: tj62
Date Posted: 01-Sep-2009 at 11:18am
Probably I'm misunderstanding you, because I cut and paste your AppIdentity2 class definition into my DomainModel project and refered it in my LoginManager.Login(...):

IIdentity identity = new C2NetDomainModel.AppIdentity2(3, pCredential.UserName);

When the login receiver method in the Silverlight application receives the return the args.Error.Message is still the same serialization complaint as I sent you when using AppIdentity2.
But if I use plain:

IIdentity identity = new GenericIdentity(pCredential.UserName,"Administrator");

then everything works.
 
So custom identities are not supported for SilverLight or what?

I will study the UserBase class tomorrow.
Best regards
  TJ


Posted By: kimj
Date Posted: 01-Sep-2009 at 11:23am
The AppIdentity2 class - or any type that is used on both client and server - needs to be "shared".  In this case, the LoginManager on the server creates the class instance; it is then sent back to the client where it's available on the EntityManager via the Principal property.  To "share" a type, include the file containing the class as a linked file in the Silverlight project; this ensures that the type definition is the same on both sides.



Print Page | Close Window