Introduction
Ceptor .NET Agent integrates Ceptor with .NET Applications or ASP.NET IIS web applications.
Ceptor .NET Agent facilitates use of Single Sign On between both Java and .NET Applications.
Using Ceptor Owin you can also use Owin and ClaimsIdentity.
Requirements
Ceptor .NET Agent requires .NET v2.0 or newer. In addition, it uses the log4net framework (freely available from http://logging.apache.org/log4net/ ) and the log4net.dll can be found in the Ceptor Distribution.
Ceptor Owin requires .NET v4.5 or newer.
Installation
Installation of Ceptor .NET Agent is fairly simple, you just need to copy PortalProtect.Agent.dll
and and log4net.dll
to to your Global Assembly Cache – usually located in c:\windows\assembly
, this makes Ceptor .NET Agent available to all other programs on your machine.
To install Ceptor Owin, you need to install Ceptor.Owin.dll
in the GAC as well.
You can also use gacutil.exe (available in Microsoft SDK) to install the DLLs.
When upgrading Ceptor, be sure to uninstall older versions – also if your configuration specifies the exact version number to use as in this example:
...
Before using Ceptor .NET Agent, you need to configure it for each application – configuration depends on how you use it. If you use Ceptor .NET from within your own standalone application, you need to configure it in app.config
and and if you use IIS, you will need to configure it in web.config
. You also have the option of setting the configuration programmatically.
Ceptor .NET Agent requires (just as its java counterpart) the name of itself, and the address of one or more configuration servers from where to retrieve its configuration.
In app.config or web.config, this is specified as follows:
And in web.config, this is specified like this:
...
In both cases, the name of the server itself, is webserver1, and the address of the configuration server is tcp://localhost:21233 – more servers can be added, separated by semicolon.
The setting nowait is optional, but if specified it tells the agent not to wait for a connection to the config server and session controller, but instead fail immediately if no connection is present.
Note that in web.config, both the property "cfgservers" and "configservers" can be used to specify the list of config servers – if cfgservers is present it will be used, otherwise configservers.
In web.config, you can change the default name of the cookies that Ceptor expects the session ID to be located within. By default, it looks at the following cookies: "sclSessionID,ppSessionID,sslsessionid,sessionid" – you can change this in appsettings by adding:
...
What this does, is to make sure that HttpContext.Current.User contains a Ceptor Principal which contains the user identification.
Note that if this HttpModule is not installed, the HttpContext.Current.User will not be correct, and some .NET applications that e.g. require role checking or authorization checks to be performed will not work.
When this module is installed, you can no longer access your website directly, but must access it via the Ceptor Dispatcher.
Note that you can always use PortalProtect.Web.HttpPP.CurrentSession() to retrieve the current session no matter if this HttpModule is installed or not – if the module is not installed, it will instead look for the Ceptor session ID in the cookie ppSessionID or sclSessionID which is one of the two default cookie names Ceptor Dispatcher uses to send the session ID to the IIS.
...
Note that Ceptor does not support the functionality that modifies roles using the Role Provider, such as creating or deleting new roles or changing role assignment.
Supported methods are: GetAllRoles(), RoleExists(), IsUserInRole() and GetRolesForUser().
For IsUserInRole() and GetRolesForUser() the username must be a session ID or the name of the user in the current session.
...
Note that the property names depend on which values you store in the session either upon login or later during use. The names above are from the Ceptor Demo Application which uses these properties to store information in.
Ceptor
...
Overview
Ceptor .NET Agent API differs slightly from the java version in syntax, but it essentially offers the same functionality.
As an example, where the java version mainly exposes a number of static methods that all takes a session ID as parameter, e.g. Agent.GetInstance().isLoggedOn(sessionid), the .NET version is more natural to the .NET world, and looks like this:
Agent.GetInstance()[sessionid].IsLoggedOn
Agent.GetInstance()[sessionid] returns an implementation of the ISession interface which exposes methods and attributes that work on this particular session.
Getting the Agent instance
To obtain an instance of an agent, simply call PortalProtect.Agent.getInstance() – this will return a configured instance of the agent which takes its configuration from web.config if running inside the IIS, and from app.config if running outside the IIS.
You can also manually create a new instance and set it as the default, by calling PortalProtect.Agent.NewInstance(servername, configservers) with the local server/service name and the list of configuration servers. To set it as default, then call PortalProtect.Agent.SetInstance(agent) with the newly created agent.
Getting a session
To get a session, call Agent.GetInstance()[sessionid] with the appropriate session ID – this will return an instance of PortalProtect.ISession which can be used to reference data within a session, or manipulate the contents of it.
If the session ID was not found – e.g. if the session timed out, it will throw a PortalProtect.SessionNotFoundException.
Getting the current session within an IIS
When running an application inside Internet Information Server, you can obtain a reference to the current session for the user by calling PortalProtect.Web.HttpPP.CurrentSession() – it will return the PortalProtect.ISession object for the current user. It does so by looking at the request cookies and obtaining the session ID from there. The requirement for it to work, is that the request first went through the Ceptor Dispatcher to the IIS server, and that the session cookie forward name in the dispatcher is set to either sclSessionID or ppSessionID. If that is not the case, it will look at the current user for the executing thread, and check if there is a session id present there. If so, it returns that session.
Manipulating state variables
Once you have a PortalProtect.ISession object, you can use the attribute ISession.StateVariables – it contains a PortalProtect.IState object which contains all current state variables. You can query, delete or add new state variables – refer to the API reference documentation for details.
Reference
Refer to PortalProtect_DotNet_Agent_Documentation.chm in the documentation directory of the Ceptor distribution for complete reference information. Start by looking at PortalProtect.Agent and PortalProtect.ISession, they will provide you with most of the information you need.
Exceptions and Errors
...
Owin
To use Ceptor Owin, you need to enable it in the IAppBuilder, this is simply done by calling the extension method UseCeptorAuthentication
on the IAppBuilder
object, like this:
Code Block |
---|
using Ceptor.Owin;
app.UseCeptorAuthentication(new CeptorAuthenticationOptions
{
MapClaimName = ((name, value) => name)
}); |
In the CeptorAuthenticationOptions
, you can choose to specify a claims name mapper delegate function/lamda - this allows you to map claim names that are added to the ClaimsPrincipal
Ceptor .NET Agent API
Overview
Ceptor .NET Agent API differs slightly from the java version in syntax, but it essentially offers the same functionality.
As an example, where the java version mainly exposes a number of static methods that all takes a session ID as parameter, e.g. Agent.GetInstance().isLoggedOn(sessionid), the .NET version is more natural to the .NET world, and looks like this:
Agent.GetInstance()[sessionid].IsLoggedOn
Agent.GetInstance()[sessionid] returns an implementation of the ISession interface which exposes methods and attributes that work on this particular session.
Getting the Agent instance
To obtain an instance of an agent, simply call PortalProtect.Agent.getInstance() – this will return a configured instance of the agent which takes its configuration from web.config if running inside the IIS, and from app.config if running outside the IIS.
You can also manually create a new instance and set it as the default, by calling PortalProtect.Agent.NewInstance(servername, configservers) with the local server/service name and the list of configuration servers. To set it as default, then call PortalProtect.Agent.SetInstance(agent) with the newly created agent.
Getting a session
To get a session, call Agent.GetInstance()[sessionid] with the appropriate session ID – this will return an instance of PortalProtect.ISession which can be used to reference data within a session, or manipulate the contents of it.
If the session ID was not found – e.g. if the session timed out, it will throw a PortalProtect.SessionNotFoundException.
Getting the current session within an IIS
When running an application inside Internet Information Server, you can obtain a reference to the current session for the user by calling PortalProtect.Web.HttpPP.CurrentSession() – it will return the PortalProtect.ISession object for the current user. It does so by looking at the request cookies and obtaining the session ID from there. The requirement for it to work, is that the request first went through the Ceptor Dispatcher to the IIS server, and that the session cookie forward name in the dispatcher is set to either sclSessionID or ppSessionID. If that is not the case, it will look at the current user for the executing thread, and check if there is a session id present there. If so, it returns that session.
Manipulating state variables
Once you have a PortalProtect.ISession object, you can use the attribute ISession.StateVariables – it contains a PortalProtect.IState object which contains all current state variables. You can query, delete or add new state variables – refer to the API reference documentation for details.
Reference
Refer to PortalProtect_DotNet_Agent_Documentation.chm in the documentation directory of the Ceptor distribution for complete reference information. Start by looking at PortalProtect.Agent and PortalProtect.ISession, they will provide you with most of the information you need.
Exceptions and Errors
The documentation mentioned above documents which methods throws which methods.
Below is an overview of common exceptions and their likely reasons.
PortalProtectException("Unable to connect to " + urls);
If nowait=true in the configuration, the agent will throw this exception if unable to contact the session controller. If nowait=false (which is the default), the thread calling the Ceptor Agent will block until a connection is available.
PortalProtectException("No connection to Session Controller, cluster: " + clusterID);
If the agent has multiple clusters of PortalProtect Servers configured, and the connection to a session controller fails, this exception will be thrown assuming nowait=true – if nowait=false, the agent will block until a connection is made.
SessionNotFoundException("No session found with ID: " + sessionID);
PortalProtect was unable to find a session with the specified ID – the session might have timed out, or it might belong to another session controller in another cluster than the one the agent is connected to.
SessionNotFoundException("Session ID " + sessionID + " is no longer valid.");
The application has a reference to an ISession object it previously retrieved and is attempting to use that session, but that session has timed out and is no longer valid.
AclNotFoundException("ACL " + aclName + " not found");
The application has called CheckPermission() on a session, but the ACL name given as input does not exist in the Authorization plugin configured on the PortalProtect Server.
SessionNotFoundException("Illegal session ID: " + requestID);
A client has attempted to call the agent with a non-valid session ID – e.g. with a username or similar instead of a session ID.
ConfigurationNotFoundException("configuration for " + serverName + " not found");
The name of the agent is not recognized by the PortalProtect Server – the server must have a configuration matching the configured name of the agent. See the configuration for the property "servername" for info.
PortalProtectException("Unable to initialize PortalProtect - requires servername specified in appSettings");
The configuration property "servername" is missing from the applications settings in app.config or web.config.
PortalProtectException("Unable to initialize PortalProtect - requires configservers specified in appSettings");
The configuration property "configservers" is missing from the applications settings in app.config or web.config.
PortalProtectException("Timeout (after " + timeout + " msecs.) waiting for reply from server for command: " + cmd.GetType().FullName);
The PortalProtect Server has not responded to a request within the time limit, check the log on the server for more information.
PortalProtectException("Unable to find applet signer certificate for provider: " + nemidProviderID);
The application has attempted to use a provider ID for generating NemID logon applet that is not configured on the PortalProtect Server. See the server configuration for a list of which Ids are configured and available for use by applications.
SessionNotFoundException("No current HTTPContext"); SessionNotFoundException("Keys: " + keys); SessionNotFoundException("No current user in HTTPContext"); SessionNotFoundException("Current user is not PPPrincipal, but " + HttpContext.Current.User.GetType()); SessionNotFoundException("No session in PPPrincipal within current HttpContext");
Various exceptions thrown by HttpPP.CurrentSession() – they either indicate that the PortalProcect HTTP Module was not installed in the IIS, or that the request did not go via a PortalProtect Dispatcher before ending up in the IIS and instead went directly to the IIS server.
Other typical errors:
In addition to these exceptions, the installed authentication plugins on the Ceptor server are capable of causing PortalProtectExceptions to be thrown by the agent – e.g. when calling Login() or Confirm().
Authentication plugins are encouraged to use error codes defined in the java interface dk.itp.security.passticket.server.AuthErrorCodes, but there is no guarantee that they do so.
Below is a list of the common error codes defined:
...
Ceptor .NET Agent provides easy access to NemID. You can get a signed applet tag with the relevant parameter by using the class PortalProtect.NemID.NemIDAppletElementGenerator.
The following is part of an example (error handling simplified) of using NemID to login (see full example in the samples/integration/dotnet directory in the Ceptor Distribution).
This snippet originates from NemIDLogin.aspx which provides the user with the NemID login applet, and processes the XMLDSIG document sent from the applet.
...
In this example, we start by checking if we are handling a POST from the applet with the signed output from the applet (or possible an error code) – and calls PortalProtect to login using the data exactly as received from the applet.
If it is not a POST, we are creating the applet tag using NemIDApletElementGenerator to do all the dirty work. It in turn calls Ceptor Server which knows all about the keys and certificates required to do the signing. See Ceptor Users Guide for details on the configuration of the server.
We basically just provide a NemID Provider ID which identifies the certificate to use, and private key to use for signing, and then wi set the URL to point to the preproduction or production NemID environment.
When that is done, we add the javascript required by the NemID Applet, and the generated applet tag and we are done.
Note that this example also generates a challenge value that is added as parameter to the applet – this challenge is then verified (done using PortalProtect.NemID.NemIDHelper) when we receive the data from the applet to ensure that the applet is responding to this particular request. If we do not do this, we risk someone recording an earlier sent login xmldsig from the applet and just replaying it to us.
When signing documents, instead of calling applet.generateLogonAppletElement() to generate the applet tag, just call applet.setSigntext() with the text to sign, and applet.generateSignAppletElement() to generate the applet tag instead. When receiving the data from the applet, call session.Confirm() instead of session.Login() – see the NemIDSign.aspx file in the sample for details.
The parameters to the applet are as described in DanIDs documentation.
Using Ceptor Owin
Ceptor Owin enables you to run a web server in a standalone process or within the IIS server.
Below is a small example of a standalone application which setups Ceptor Authentication - this will then expect that the application is no longer called directly, but always via a Ceptor Gateway - it looks for the session ID in the configured cookies, and picks out the user information and provides all state variables as claims within a ClaimsPrincipal.
The MapClaimName
delegate in the CeptorAuthenticationOptions
makes it possible for you to map claim names if you want specific state variables in the session to be mapped to specific claims. If you return null, the specific claim will be ignored and not added to the Identity.
Below is an example
Code Block |
---|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Owin.Hosting;
using Owin;
using Ceptor.Owin;
using PortalProtect;
using System.Security.Claims;
namespace Ceptor.Owin.Test
{
public class Class1
{
static void Main(string[] args)
{
Console.WriteLine("Attempting to start small local webserver on port 12345");
// Not needed, but creates a session just to verify that the connection to Ceptor server works
ISession session = Agent.GetInstance().CreateSession("127.0.0.1");
Console.WriteLine("Created new session: " + session.SessionID);
using (WebApp.Start<Startup>("http://localhost:12345"))
{
Console.ReadLine();
}
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCeptorAuthentication(new CeptorAuthenticationOptions
{
MapClaimName = ((name, value) => name)
});
app.Map("/test", config =>
{
config.Run(context =>
{
context.Response.ContentType = "text/html";
if (context.Request.User != null)
{
ClaimsPrincipal cp = context.Request.User as ClaimsPrincipal;
Console.WriteLine("isAuthenticated: " + cp.Identity.IsAuthenticated);
Console.WriteLine("Claims: " + cp.Claims);
foreach (var claim in cp.Claims)
{
Console.WriteLine(claim.Type + "=" + claim.Value);
}
}
return context.Response.WriteAsync("<html><body>test</body></html>");
});
});
app.UseWelcomePage("/");
}
}
} |
Using Authorization
Configuring Authorization checks
...
Programmatic Authorization
Once you obtained a PortalProtect.ISession object, you can call CheckPermission() to check if the user as the given ACL permission, or you can call IsMemberOfGroup() to check group membership. If you want to find out if access to an URL is allowed, call IsUrlAllowed() to check if the user in the session has access to that particular URL. See the reference documentation for details. You can also call IsInRole() on the current windows user principal, assuming the Ceptor .NET Agent HttpModule is installed as described earlier in this document.
...