DevForce® Classic Tech Tips
Authentication in DevForce 



Authentication in DevForce Level 300
DevForce Express
July 27, 2007

[Note: The code samples in this Tech Tip are extracted and modified from the code solutions in the instructional units Security_Authentication and Security_Role-Based Authorization. We don’t attempt herein to explain exactly where each variable comes from, but rather to provide just enough context to illustrate a point. See the instructional units for the complete context and details.]

To perform authentication, you invoke the Login() method on a PersistenceManager, passing it an ILoginCredential object:


C#

using IdeaBlade.Persistence;

userName = mUserNameTextBox.Text;
password = mPasswordTextBox.Text;
domain = mDomainTextBox.Text;
LoginCredential aCredential = new LoginCredential(
  userName,
  IdeaBlade.Util.CryptoFns.MD5HashUTF16ToString(password),
  domain);

aPersistenceManager.Login(aCredential);

 

VB

Imports IdeaBlade.Persistence

userName = mUserNameTextBox.Text
password = mPasswordTextBox.Text
domain = mDomainTextBox.Text
Dim aCredential As LoginCredential = New LoginCredential( _
  userName, IdeaBlade.Util.CryptoFns.MD5HashUTF16ToString(password), domain)

aPersistenceManager.Login(aCredential)

 

Internally, PersistenceManager.Login() forwards the login request to the PersistenceServer, which looks for a class that implements the DevForce IPersistenceLoginManager interface. That interface requires only a single member – a Login() method, which takes an ILoginCredential and a PersistenceManager as inputs:

The PersistenceServer looks for this class by reflecting through the probe assemblies associated with the datasource keys found in ibconfig:

   <rdbKey name="default" databaseProduct="Unknown">
    <connection>Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security
Info=False;User ID=sa;Initial Catalog=IdeaBladeTutorial;Data Source=.</
connection>
    <probeAssemblyName>Model</probeAssemblyName>
    <probeAssemblyName>Server</probeAssemblyName>
  </rdbKey>

For the above-illustrated datasource key, assemblies named “Model” and “Server” are named as probe assemblies, so the PersistenceServer reflects against those assemblies looking for a class that implements IPersistenceLoginManager.  If it finds one, it calls the Login() method in that class to perform the actual authentication, passing on both the credential information that your code provided to it, and a reference to a server-side PersistenceManager that the method can use if needed.

Since it is you who provides the implementation of IPersistenceLoginManager, you write its Login() method to perform authentication by any means you desire. In the “Security_Authentication” instructional unit, a sample implementation of IPersistenceLoginManager.Login() performs authentication by looking up in a database the user named in the credential, then comparing a hashed password stored in the database for that user with the hashed password included in the credential. If they match, the method returns an IPrincipal object that identifies the authenticated user. If they don’t match, it throws an exception:


C#

  if (pCredential == null) {
    throw new LoginException(LoginExceptionType.NoCredentials, null, null);
  } 

  if (pUser.IsNullEntity) {
    throw new LoginException(LoginExceptionType.InvalidUserName, null, pUsername);
  } 

  if (pPassword != pUser.UserPassword) {
    throw new LoginException(LoginExceptionType.InvalidPassword, null, pUsername);
  } 

  return principal; 

 

VB

  If pCredential Is Nothing Then
   Throw New LoginException(LoginExceptionType.NoCredentials, Nothing, Nothing)
  End If 

  If pUser.IsNullEntity Then
   Throw New LoginException(LoginExceptionType.InvalidUserName, Nothing, pUsername)
  End If 

  If pPassword <> pUser.UserPassword Then
   Throw New LoginException(LoginExceptionType.InvalidPassword, Nothing, pUsername)
  End If 

  Return principal

The PersistenceServer either forwards the exception to the client-side code that called PersistenceManager.Login(), or wraps the IPrincipal object in another DevForce object called a SessionBundle, and returns that to PersistenceManager.Login(). Independent of anything you do or don’t do with that SessionBundle, it is kept around by the PersistenceManager, which passes it to the PersistenceServer as part of each subsequent contact.  The PersistenceServer uses the SessionBundle to authenticate the incoming request from the client.

You may or may not find a use yourself for the returned SessionBundle. It does include a Principal property, which contains an IPrincipal object that represents the authenticated user. That is of some interest, since it may contain additional information about that user previously unknown to the client.  (See the discussion below on attaching role information.)

On the other hand, upon successful login, the PersistenceManager also attaches the same IPrincipal to the running thread, whence it may subsequently be accessed as follows:

C#

 authenticatedPrincipal=System.Threading.Thread.CurrentPrincipal;

 

 VB

 authenticatedPrincipal=System.Threading.Thread.CurrentPrincipal

So, if you like, you can quite safely ignore the SessionBundle returned by Login().

Attaching Role Information to the Authenticated IPrincipal

When you authenticate your user in the IPersistenceLoginManager.Login() method, you may also wish to their assigned security roles and attach those to the returned IPrincipal so it can be queried for them.  The code below gets the role information from a User business object whose class was generated by the DevForce Object Mapper from the schema of a User table.  [Note: see the User, UserRole, and Role tables in the IdeaBladeTutorial database.]

C#

 public IPrincipal Login(ILoginCredential pCredential, PersistenceManager pManager) { 

  IPrincipal principal = new GenericPrincipal(identity, GetRoles(pManager,username)); 

  return principal;
}

 private string[] GetRoles(PersistenceManager pManager, string pUserName) {
  RdbQuery anRdbQuery = new RdbQuery(typeof(User));
  anRdbQuery.AddClause(User.UserNameEntityColumn, RdbQueryOp.EQ, pUserName);
  User aUser = pManager.GetEntity<User>(anRdbQuery);
  EntityList<Role> roles = aUser.Roles;  

  System.Collections.ArrayList roleNames = new System.Collections.ArrayList();
  foreach (Role aRole in aUser.Roles) {
    roleNames.Add(aRole.Name);
  }
  return (String[])roleNames.ToArray(typeof(String));
} 

 

VB

Public Function Login(ByVal pCredential As ILoginCredential, _
    ByVal pManager As PersistenceManager) As IPrincipal 

  IPrincipal principal = New GenericPrincipal(identity, GetRoles(pManager,username)) 

Return principal

End Function 

Private Function GetRoles(ByVal pManager As PersistenceManager, _
    ByVal pUserName As String) As String()
  Dim anRdbQuery As RdbQuery = New RdbQuery(GetType(User))
  anRdbQuery.AddClause(User.UserNameEntityColumn, RdbQueryOp.EQ, pUserName)
  Dim aUser As User = pManager.GetEntity(Of User)(anRdbQuery)
  Dim roles As EntityList(Of Role) = aUser.Roles 

  Dim roleNames As System.Collections.ArrayList = New System.Collections.ArrayList()
  For Each aRole As Role In aUser.Roles
   roleNames.Add(aRole.Name)
  Next aRole
  Return CType(roleNames.ToArray(GetType(String)), String())
End Function

 In your UI, you can use this information about role memberships to suppress or display particular features or capabilities:


C#

private void LoadUserUserControl() {
  if (!authenticatedPrincipal.IsInRole("admin")) {
    return;
  } else {
    ConstructUserTab();
    UserUserControl aUserUserControl = new UserUserControl();
    mUsersPanel.Controls.Add(aUserUserControl);
    aUserUserControl.InitializeUserControl();
  }
} 

 

VB

 Private Sub LoadUserUserControl()
  If (Not authenticatedPrincipal.IsInRole("admin")) Then
   Return
  Else
   ConstructUserTab()
   Dim aUserUserControl As UserUserControl = New UserUserControl()
   mUsersPanel.Controls.Add(aUserUserControl)
   aUserUserControl.InitializeUserControl()
  End If
End Sub 

This concludes our introduction to authentication in DevForce There are many other aspects to DevForce’s security facilities: we’ll address these in future Tech Tips. Stay tuned!