Introduction
Ceptor PortalProtect .NET Agent integrates PortalProtect Ceptor with .NET Applications or ASP.NET IIS web applications.
Ceptor PortalProtect .NET Agent facilitates use of Single Sign On single sign-on between both Java and .NET Applications.
Using Ceptor Owin you can also use Owin and ClaimsIdentity.
Requirements
PortalProtect 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 PortalProtect DistributionCeptor Distribution.
Ceptor Owin requires .NET v4.5 or newer.
Installation
Installation of Ceptor PortalProtect .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 PortalProtect Ceptor .NET Agent available to all other programs on your machine. When upgrading PortalProtect, be sure to uninstall older versions – also if your configuration specifies the exact
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:
...
Then you need to make sure to change the version number to the newly installed PortalProtect Ceptor.NET Agent version.
Configuration
Before using PortalProtect Ceptor .NET Agent, you need to configure it for each application – configuration depends on how you use it. If you use PortalProtectCeptor .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.
PortalProtect 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 PortalProtect 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:
...
Code Block | ||
---|---|---|
| ||
<?xml version="1.0" encoding="UTF-8"?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler" /> </configSections> <system.web> <compilation debug="true" /> <authentication mode="Forms"> <forms loginUrl="~/Account/Login.aspx" timeout="2880" /> </authentication> <membership defaultProvider="PPMembershipProvider"> <providers> <clear /> <add name="PPMembershipProvider" type="PortalProtect.Providers.PortalProtectMembershipProvider" applicationName="/" /> </providers> </membership> <profile enabled="true" defaultProvider="PPProfileProvider"> <providers> <add name="PPProfileProvider" type="PortalProtect.Providers.PortalProtectProfileProvider" /> </providers> <properties> <add name="email1" defaultValue="[null]" /> <add name="firstname" defaultValue="x" /> <add name="lastname" defaultValue="[null]" /> <add name="address1" defaultValue="[null]" /> <add name="city" defaultValue="[null]" /> <add name="zipcode" defaultValue="[null]" /> <add name="country" defaultValue="[null]" /> <add name="phone" defaultValue="[null]" /> <add name="phonemobile" defaultValue="[null]" /> </properties> </profile> <roleManager enabled="true" defaultProvider="PPRoleProvider"> <providers> <clear /> <add name="PPRoleProvider" type="PortalProtect.Providers.PortalProtectRoleProvider" applicationName="/" /> </providers> </roleManager> <customErrors mode="Off" /> </system.web> <appSettings> <add key="servername" value="webserver1" /> <add key="cfgservers" value="nio://localhost:21233" /> <add key="nowait" value="true" /> </appSettings> <system.webServer> <modules> <add name="PPAuth" type="PortalProtect.Providers.PPBasicAuthenticationModule, PortalProtect.Agent, Version=1.0.0.0, Culture=neutral, PublicKeyToken=218ecb9b9450d01f" /> </modules> </system.webServer> <log4net> <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender"> <applicationName value="APP - PortalProtect"/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline"/> </layout> </appender> <appender name="PortalProtectAppender" type="PortalProtect.Log.RemoteLogAppender"> <servers value="nio://localhost:21236"/>" </appender> <!-- Setup the root category, add the appenders and set the default level --> <root> <level value="INFO" /> <appender-ref ref="EventLogAppender" /> <appender-ref ref="PortalProtectAppender" /> </root> </log4net> </configuration> |
Configurations for
...
Testing
If needed for offline testing, PortalProtect Ceptor .NET Agent supports use of an alternate dummy version of the agent, which implements the IAgent interface, but does not provide real data.
...
This agent allows creation of a new session, and storing / retrieving state date from it, but the session is only available internally in the .NET agent and definitely not meant for production use. But, if you need to test without having a real PortalProtect Ceptor instance available, this can help you.
Note that this also requires PortalProtect.Agent.Dummy.dll to be available to the application.
If you have special requirements for the behaviour of the agent, you can create your own implementation of IAgent and reference the generated type.
HttpModule
PortalProtect Ceptor .NET Agent HttpModule (PortalProtect.Providers.PPBasicAuthenticationModule) looks at all HTTP requests, and requires basic authentication. This means that it always assumes that a PortalProtect Ceptor dispatcher is present in front of the IIS, since the dispatcher by default forwards the session ID in the HTTP authorization header, which is used for basic authentication.
The PortalProtect Ceptor .NET Agent HttpModule is enabled by adding this to your web.config :
...
What this does, is to make sure that HttpContext.Current.User contains a PortalProtect 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 PortalProtect 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 PortalProtect Ceptor session ID in the cookie ppSessionID or sclSessionID which is one of the two default cookie names PortalProtect Ceptor Dispatcher uses to send the session ID to the IIS.
...
Note that only some of the functionality within a membership provider is supported. Operations that manipulate users, such as creating, or deleting them is not supported. Also quering querying users via the membership provider is currently not supported.
ValidateUser() with userid/password authentication is supported, and ChangePassword() is supporteded depending supported depending on the authentication plugin which the current user is authenticated with, since it might not support changing of passwords.
Authentication
...
Mode
The preferred authentication in web.config is None, set as follows:
...
This tells the IIS to use custom authentication, usually provided by installing the PortalProtect Ceptor .NET HttpModule.
Use of Forms Login and
...
Logging off
On occationoccasion, Forms Login is used together with Membership Provider – there is no requirement to use it with PortalProtect Ceptor Membership Provider, but if you do you need to add something like this to your web.xml:
...
You should note that forms authentication uses its own cookie to track authentication, which conflicts with PortalProtectCeptor, so you need to keep it in sync with PortalProtects Ceptors session.
If you e.g. use an ASP.NET LoginView like the following, you need to add OnLoggingOut which looks like the following:
...
The OnLoggingOut must call the method which logs off PortalProtect Ceptor session too.
Role Provider
The Role Provider allows access to the list of a users a users roles, or all available roles in the system.
The Role Provider is enabled by adding the following to web.config:
Code Block | ||
---|---|---|
| ||
<roleManager enabled="true" defaultProvider="PPRoleProvider"> <providers> <add name="PPRoleProvider" type="PortalProtect.Providers.PortalProtectRoleProvider" applicationName="/" /> </providers> </roleManager> |
Note that PortalProtect 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.
...
The profile provider allows access to state variables with the PortalProtect Ceptor session. A state variable is a string stored in the session that can contain anything, from the users address, email, phone number to information about what credentials the user has for other systems, userids for backend systems or other business related information such as the maximum amount the user is allowed to withdraw from an account per day or create loans for without further approval.
The profile provider is enabled by adding the following to web.config:
...
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 PortalProtect Ceptor Demo Application which uses these properties to store information in.
PortalProtect .NET Agent API
Overview
PortalProtect .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
...
Ceptor 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] 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 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 PortalProtect 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 PortalProtect 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.
...
The documentation mentioned above documents which methods throws throw 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 PortalProtect 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 PortalProtect 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:
...
The relevant error code is available in the "ErrorCode" property on the PortalProtectException, and if the authentication plugin provides an "ErrorMessage" meant for displaying to the user, this is available too in the "ErrorMessage" property.
NemID Integration
PortalProtect 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 PortalProtect 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 PortalProtect Ceptor Server which knows all about the keys and certificates required to do the signing. See PortalProtect 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 Authorization
...
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
Configuration-based authorization checks rely on standard .NET mechanisms which uses current principal, so for them to work, the PortalProtect Ceptor .NET Agent HttpModule must be enabled for the application.
You can use e.g. <authorization> tags in the web.config file (see Microsofts documentation for details), or you can authorize individual methods like described here: http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.aspx
Example
...
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 PortalProtect Ceptor .NET Agent HttpModule is installed as described earlier in this document.
...
… or you can configure the PortalProtect Ceptor Dispatcher/Gateway to send that state in the HTTP headers.
...