New Posts New Posts RSS Feed: Generic Identity Authorisation in 2-tier
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Generic Identity Authorisation in 2-tier

 Post Reply Post Reply
Author
jradxl View Drop Down
Newbie
Newbie


Joined: 14-Jun-2011
Posts: 30
Post Options Post Options   Quote jradxl Quote  Post ReplyReply Direct Link To This Post Topic: Generic Identity Authorisation in 2-tier
    Posted: 18-Jun-2011 at 2:43pm
Generic Identity Authorisation in 2-tier

Hi,
Using your example Console_SecurityWindowsAuthentication, which I have arranged to be 2-tier, I am adding code to support non-Windows Generic Identity Authorization.

In your code example of IEntityLoginManager you have a LoginException being generated on the Server side (which is of course in 2-tier also running on the client), if the login fails.

On the otherhand, if the GenericIdenity.IsAuthenticated is false, the login as seen by the Client still succeeds.

Also, EntityServerQueryInterceptor is probed for after the login is completed.

The only way I can find of returning a failed login back to the Client is to return a null UserBase. This causes a EntityServerException of "Session Bundle is invalid". Which matches your code example.
Only in the Catch blocks is EntityManager.IsLoggedIn false.

I'd much prefer to have LoginExceptionType.InvalidUserName returned to the Client.
Please can you suggest a way?

Alternatively, can you support:-
                var loginResult = mgr.Login((ILoginCredential)cred);
                var isLoggedIn = mgr.IsLoggedIn;

as false, when the login fails. So that one can test in the Client, and not have an exception.

//Client
        void DoIt()         {             NorthwindManager mgr = new NorthwindManager();             //IdeaBlade.Core.IdeaBladeConfig.ConfigFileAssembly = Assembly.GetExecutingAssembly();             var configInstance = IdeaBlade.Core.IdeaBladeConfig.Instance;             configInstance.ObjectServer.ClientSettings.IsDistributed = false;             configInstance.ObjectServer.ServerSettings.AllowAnonymousLogin = false;             configInstance.ObjectServer.ServerSettings.LoginManagerRequired = true;             configInstance.Logging.ShouldLogSqlQueries = true;             configInstance.Logging.LogFile = "MyLogFile.xml"; //not working.             Console.WriteLine("Thread principal before login: {0}", System.Threading.Thread.CurrentPrincipal.Identity.Name);             bool isLoggedIn = false;             bool loginResult = false;             try             {                 // Login will null credentials.  The LoginManager will decide what to do ...                 LoginCredential cred = new LoginCredential("john""password""domain");                 //loginResult = mgr.Login((ILoginCredential)null);                  loginResult = mgr.Login((ILoginCredential)cred);                 isLoggedIn = mgr.IsLoggedIn;                 Console.WriteLine("Thread principal after login: {0}", System.Threading.Thread.CurrentPrincipal.Identity.Name);             }             catch (LoginException le)             {                 Console.WriteLine("LoginException: " + le.Message);                 isLoggedIn = mgr.IsLoggedIn;                 Console.WriteLine("Press ENTER to exit...");                 Console.ReadLine();                 return;             }             catch (EntityServerException ese)             {                 Console.WriteLine("EntityServerException Login failed: " + ese.Message);                 isLoggedIn = mgr.IsLoggedIn;                 Console.WriteLine("Press ENTER to exit...");                 Console.ReadLine();                 return;             }             catch (Exception ex)             {                 bool isLogged = mgr.IsLoggedIn;                 Console.WriteLine(ex.Message);                 return;             }

// etc....
         }

//Server
        public IPrincipal Login(ILoginCredential credential, EntityManager entityManager)         {             if (credential == null)             {                 // if running n-tier the user's Windows credentials will be passed and available on the thread:                 var principal = System.Threading.Thread.CurrentPrincipal;                 if (principal.Identity.IsAuthenticated)                 {                     Logger.WriteMsg(string.Format("principal is {0}", principal.Identity.Name));                     // Note that we can't return the WindowsPrincipal/WindowsIdentity back to the client, so                     // create a new one, here we use the DevForce UserBase class, but you can create your own too.                      return CreateUser(principal);                 }                 else                 {                     // This is here for 2-tier testing, since you'll probably want to throw an error or do                     // some custom logic in n-tier.  When not running n-tier, the thread principal has not been set                      // but you can obtain the Windows Identity and use that.                     var wi = WindowsIdentity.GetCurrent();                     Logger.WriteMsg(string.Format("wi is {0}", wi.Name));                     WindowsPrincipal wp = new WindowsPrincipal(wi);                     return CreateUser(wp);                 }             }             else             {                 Logger.WriteMsg("Non-Windows Login");                 GenericIdentity gi = null;                 if (credential.UserName.Equals("john")) //change to somthing else to force failure                 {                     gi = new GenericIdentity(credential.UserName, "MyAuthoType");                 }                 else                 {
//This is the way of getting IsAuthenticated set to false                     gi = new GenericIdentity("""MyAuthoType");                     //This will throw in EntityServer                     // throw new LoginException(LoginExceptionType.InvalidUserName, credential.Domain, credential.UserName);                 }                 GenericPrincipal gp = new GenericPrincipal(gi, null);                 Logger.WriteMsg("Non-Windows Login Completed.");                 return CreateUser(gp);             }             throw new NotImplementedException("Some Login Error");         }         private UserBase CreateUser(IPrincipal currentUser)         {             // The UserBase and UserIdentity classes are custom IPrincipal/IIdentity implementations             // within DevForce.               UserIdentity identity = new UserIdentity(currentUser.Identity.Name, currentUser.Identity.AuthenticationType, currentUser.Identity.IsAuthenticated);             bool isInRole = currentUser.IsInRole("");             var isAuth = currentUser.Identity.IsAuthenticated;             // You can determine what roles to set, if any.             string[] roles = new string[] { };             if (isAuth)             {                 Logger.WriteMsg("UserBase created.");                 return new UserBase(identity, roles);             }             //Will result in an EntityServerException in the Client             Logger.WriteMsg("Null UserBase returned.");             return null;         }

thanks
John



Edited by jradxl - 18-Jun-2011 at 3:12pm
Back to Top
DenisK View Drop Down
IdeaBlade
IdeaBlade


Joined: 25-Aug-2010
Posts: 715
Post Options Post Options   Quote DenisK Quote  Post ReplyReply Direct Link To This Post Posted: 22-Jun-2011 at 5:03pm
Hi jradxl;

I'd much prefer to have LoginExceptionType.InvalidUserName returned to the Client.
Please can you suggest a way?

To do this, you can throw a LoginException back to the client.

throw new LoginException(LoginExceptionType.InvalidUserName, credential.Domain, credential.UserName);

I see that you commented that part of the code out and you create a GenericIdentity instead with IsAuthenticated = false. A login and authentication are 2 different processes. A user can successfully login but not authenticated. To disallow login, a LoginException needs to be thrown.

Alternatively, can you support:-
                var loginResult = mgr.Login((ILoginCredential)cred);
                var isLoggedIn = mgr.IsLoggedIn;

as false, when the login fails. So that one can test in the Client, and not have an exception.

Currently, as I've noted above, to indicate a failed login, one has to throw a LoginException. If you have a convincing use case for this, I might be able to discuss this with a senior engineer and add it to the feature request list.
Back to Top
jradxl View Drop Down
Newbie
Newbie


Joined: 14-Jun-2011
Posts: 30
Post Options Post Options   Quote jradxl Quote  Post ReplyReply Direct Link To This Post Posted: 23-Jun-2011 at 11:44am
Hello DenisK,
Thank you for your time in search back to my question.

I'm sorry, but I do not find,
throw new LoginException(LoginExceptionType.InvalidUserName, credential.Domain, credential.UserName);
returning to the Client,
as this DebugLog file below shows. The application stops dead at the throw statement.

I guess that calling Login is early in your library's initialisation, and the method for returning to the Client is not available.

thanks
John

<?xml version="1.0"?><?xml-stylesheet href="DebugLog.xsl" type="text/xsl"?>
<log>
<entry id="14" timestamp="2011-06-23T19:36:20" username="" source="IdeaBlade.Core.TraceFileXmlLogger:GetLogHeader">------------ Log Created ------------</entry>
<entry id="0" timestamp="2011-06-23T19:36:15" username="" source="Console01.Program:DoIt">TraceFns: Program Starting...</entry>
<entry id="1" timestamp="2011-06-23T19:36:15" username="" source="Console01.Program:DoIt">DebugFns: Program Starting...</entry>
<entry id="2" timestamp="2011-06-23T19:36:15" username="" source="IdeaBlade.Core.IdeaBladeConfig:Initialize">Initializing configuration ...</entry>
<entry id="3" timestamp="2011-06-23T19:36:17" username="" source="IdeaBlade.Core.Composition.PartsCatalog:LoadCatalog">MEF assembly probing started: 23/06/2011 19:36:17.  If this takes a long time, use IdeaBlade.Core.Composition.CompositionHost to specify/restrict which assemblies to probe.</entry>
<entry id="4" timestamp="2011-06-23T19:36:17" username="" source="IdeaBlade.Core.Composition.PartsCatalog:LoadCatalog">Assembly 'Console01, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' added to PartsCatalog</entry>
<entry id="5" timestamp="2011-06-23T19:36:17" username="" source="IdeaBlade.Core.Composition.PartsCatalog:LoadCatalog">Error loading assembly 'C:\VSProjects\VS2010\DevForce\Console_SecurityWindowsAuthentication\080_Secure\Console_SecurityWindowsAuthentication-2TierNoServer\CodeCS\Console01\bin\Debug\Console01.exe.config' for PartsCatalog: Error with file Console01.exe.config.</entry>
<entry id="6" timestamp="2011-06-23T19:36:17" username="" source="IdeaBlade.Core.Composition.PartsCatalog:LoadCatalog">MEF assembly probing completed: 23/06/2011 19:36:17</entry>
<entry id="7" timestamp="2011-06-23T19:36:17" username="" source="IdeaBlade.Core.Composition.CompositionHost:.ctor">Probe Assemblies: Console01, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null :: SimpleNorthwindModel.Desktop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null :: WPFTraceViewer, Version=6.1.0.0, Culture=neutral, PublicKeyToken=287b5094865421c0  If this list is unnecessarily large, use CompositionHost.SearchPatterns to modify the search critieria for probed assemblies. If this list does not contain the assembly(ies) holding your a) domain model, b) custom interface implementations, and c) POCO/known types then your application may not work correctly. Ensure that these assemblies are available in the exe/bin folder, and if using CompositionHost.SearchPatterns that the patterns are set appropriately.</entry>
<entry id="8" timestamp="2011-06-23T19:36:17" username="" source="IdeaBlade.Core.IdeaBladeConfig:InitializationStatusCallback">IdeaBlade License: 'EnterpriseUniv, Express', KeyDate: 15/04/2010, AllowedSessions: 10000. Found on Assembly: 'SimpleNorthwindModel.Desktop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'</entry>
<entry id="9" timestamp="2011-06-23T19:36:17" username="" source="IdeaBlade.Core.Configuration.ServerSettingsElement:ConstrainSettingsBasedOnLicenseCore">The configured SupportedClientApplicationType is Both</entry>
<entry id="10" timestamp="2011-06-23T19:36:17" username="" source="IdeaBlade.Core.Configuration.ServerSettingsElement:ConstrainSettingsBasedOnLicenseCore">Could not find a valid license to enable session-agnostic load balancing.</entry>
<entry id="11" timestamp="2011-06-23T19:36:20" username="" source="IdeaBlade.Core.Configuration.ServerSettingsElement:ConstrainSettingsBasedOnLicenseCore">Could not find a valid license to enable session-agnostic load balancing.</entry>
</log>

Back to Top
DenisK View Drop Down
IdeaBlade
IdeaBlade


Joined: 25-Aug-2010
Posts: 715
Post Options Post Options   Quote DenisK Quote  Post ReplyReply Direct Link To This Post Posted: 23-Jun-2011 at 4:25pm
Hi John;

Hmm, that's strange. Here's my sample solution that shows that LoginException is caught on the client.


Hopefully you'll be able to see the difference. If not, feel free to upload your solution to me and I'll see what I can do.
Back to Top
jradxl View Drop Down
Newbie
Newbie


Joined: 14-Jun-2011
Posts: 30
Post Options Post Options   Quote jradxl Quote  Post ReplyReply Direct Link To This Post Posted: 24-Jun-2011 at 1:26am
Hello Denis,
Thanks for code sample. Unfortunately it runs with same issue on my laptop - throws in LoginManager on the Server.
I've upgraded to 6.1.1 from 6.1.0. No change.

What version of DF did you compile and run against? Please attach copy of your DebugLog after a failed login.

thanks
John

Back to Top
DenisK View Drop Down
IdeaBlade
IdeaBlade


Joined: 25-Aug-2010
Posts: 715
Post Options Post Options   Quote DenisK Quote  Post ReplyReply Direct Link To This Post Posted: 24-Jun-2011 at 2:29pm
Hi John;

I tried version 6.1.0 and 6.1.1. However, I don't think it has anything to do with the version. I do hope though that I'm not misunderstanding your question.

To clarify, you want a LoginException thrown on the server and caught on the client. In my code sample, I have the following:

CLIENT

      try {
        // Login will null credentials.  The LoginManager will decide what to do ...
        //loginResult = mgr.Login((ILoginCredential)null);

        LoginCredential cred = new LoginCredential("johnxxx", "password", "domain");
        var loginResult = mgr.Login((ILoginCredential)cred);
        var isLoggedIn = mgr.IsLoggedIn;
        Console.WriteLine("Thread principal after login: {0}", System.Threading.Thread.CurrentPrincipal.Identity.Name);
      } catch (LoginException le) {
        Console.WriteLine("LoginException: " + le.Message);
        var isLoggedIn = mgr.IsLoggedIn;
        Console.WriteLine("Press ENTER to exit...");
        Console.ReadLine();
        return;
      }

SERVER

    public IPrincipal Login(ILoginCredential credential, EntityManager entityManager) {

      if (credential == null) {
        #region If Credential is null
        //details omitted
        #endregion
      } else {
        Console.WriteLine("Non-Windows Login");
        GenericIdentity gi = null;
        if (credential.UserName.Equals("john")) { //change to somthing else to force failure
          gi = new GenericIdentity(credential.UserName, "MyAuthoType");
        } else {
          throw new LoginException(LoginExceptionType.InvalidUserName, credential.Domain, credential.UserName);
        }
        GenericPrincipal gp = new GenericPrincipal(gi, null);
        Console.WriteLine("Non-Windows Login Completed.");
        return CreateUser(gp);
      }

      //throw new NotImplementedException("Only Windows authentication is supported here");
    }

CONSOLE OUTPUT

Thread principal before login:
Non-Windows Login
LoginException: 'johnxxx' is not a user name in the 'domain' domain.
Press ENTER to exit...

Is this not what you're seeing and/or expecting?

I've attached my debug log here. uploads/912/Debug.zip
Back to Top
jradxl View Drop Down
Newbie
Newbie


Joined: 14-Jun-2011
Posts: 30
Post Options Post Options   Quote jradxl Quote  Post ReplyReply Direct Link To This Post Posted: 24-Jun-2011 at 3:43pm
Hi Denis,

I see where I was going wrong. I was running in the VS2010 debugger, and it stopped at the throw line.
If I run Console01.exe from the bin\Debug directory, it works.

I added two more Console.WriteLine statements, and it does get back to the Client, as shown.

Thread principal before login:
Non-Windows Login
About to Throw on Server...
In Client Catch
LoginException: 'johnx' is not a user name in the 'domain' domain.
Press ENTER to exit...

I was expecting the Debuger to find the Catch statement in the Client.
Sorry. I assume you were running the exe directory?

rgds
John

Back to Top
DenisK View Drop Down
IdeaBlade
IdeaBlade


Joined: 25-Aug-2010
Posts: 715
Post Options Post Options   Quote DenisK Quote  Post ReplyReply Direct Link To This Post Posted: 27-Jun-2011 at 11:54am
Hi John;

Actually, I was running this from the debugger. I think this is just a matter of setting the right options on Debug -> Exceptions on your VS2010.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down