|
Yes, in this version of Cabana, Ward has factored client-side authentication into the AppAuthenticationService, implementing IAuthentication service. It's in the Foundation project.
IAuthentication service exposes one method, Authenticate(), that is invoked at application startup. This calls Login() to first get the user's credentials, then attempt to login via the persistence manager:
/// <summary>Login this manager.</summary>
protected virtual void Login(PersistenceManager pManager) {
ILoginCredential aCredential = GetCredential();
Login(pManager, aCredential);
}
In GetCredential() we see:
/// <summary>Get the credential for this user.</summary>
/// <remarks>
/// This implementation only knows how to get a Windows Credential;
/// see <see cref="GetWindowsCredential"/>.
/// </remarks>
protected virtual ILoginCredential GetCredential() {
ILoginCredential credential = GetWindowsCredential();
if (credential == null) {
// We don't have an alternative way to get the credential
throw new InvalidOperationException("Windows user is not authenticated.");
}
return credential;
}
It is here that you could write code to display a login form and create an object implementing the DevForce ILoginCredential interface. The class IdeaBlade.Persistence.LoginCredential is the base implementation of this interface.
On the server side, the credentials are passed to the Login() method of the LoginManager (in the Model project). There, the method GetAppIdentity() checks for a non-empty user name (an empty user name signals that Windows authentication should be used) and invokes GetUserPasswordIdentity():
/// <summary>
/// Get user based on user LoginName and password
/// </summary>
/// <param name="pCredential">Login credential.</param>
/// <param name="pManager">PersistenceManager for retrieving user.</param>
/// <returns>Identity built based on lookup of user's LoginName and password.</returns>
[ DebuggerNonUserCode] // Let client catch exceptions
private static AppIdentity GetUserPasswordIdentity(ILoginCredential pCredential, PersistenceManager pManager) {
IUser aUser = SecurityUser.GetUserByCredential(pManager, pCredential);
return new AppIdentity(aUser.FullName, aUser.Id, "UserPassword");
} Here we must supply an implementation of IUser from somewhere. In Cabana (and wizard-based applications), we rely on the existence of an entity in the model called SecurityUser that has a static factory method GetUserByCredential(). In the Cabana implementation, this method retrieves the user from the database by username and password. You can provide code that authenticates the user and returns an implementation of IUser.
Back in the Login(credential, pm) method of LoginManager, once the user is authenticated and we have an AppIdentity object, GetUserRoles() is invoked and a Principal object created:
public IPrincipal Login(ILoginCredential pCredential, PersistenceManager pManager) {
AppIdentity identity = GetAppIdentity(pCredential, pManager);
String[] roles = GetUserRoles(pManager, identity);
IPrincipal principal = new GenericPrincipal(identity, roles);
}
In GetUserRoles() you need to provide the authorized roles for the user as an array of strings:
/// <summary>
/// Get the user's roles
/// </summary>
/// <param name="pManager">PersistenceManager for retrieving roles from persistent storage.</param>
/// <param name="pIdentity">Identity of the user who has the roles.</param>
private static string[] GetUserRoles(PersistenceManager pManager, AppIdentity pIdentity) {
// Todo: Get the roles via the UserId in the identity.
// pIdentity.AuthenticationType may figure in role determination.
return new string[] { };
}
These will be placed in the Principal object and returned to the client, where the Principal will be set as the user of the application.
By placing
<Roles>
<Role Allow="rolename"/>
</Role>
tags within Modules in the ProfileCatalog.xml file, you cause CAB to check the user's roles [via IPrincipal.IsInRole()] before loading the module.
You are also free to include role based security to restrict functionality within your code. See:
http://msdn2.microsoft.com/en-us/library/aa720542%28VS.71%29.aspx - http://msdn2.microsoft.com/en-us/library/aa720542(VS.71).aspx
Hope this answers your questions.
Bill J
|