Implements a set of classes that control application access to
abstract resources and manage security credentials.
Author
|
Nick Saxon
Greg Shah
|
Date
|
October 14, 2011
|
Access Control
|
CONFIDENTIAL
|
Contents
Introduction
Security Context
Authentication
Resources
Security Database
Access
Rights Check
Builtin Resource Type "system"
Auditing
Configuration
Manageability
API Summary
Appendix A. Security UtilitiesIntroduction
This package provides application level security for the P2J
environment. It is important to understand that this kind of
security
solution is not, and cannot be, make up for poor operating
system level security. Application level security
is a
means of protecting application data and logic from unauthorized access
or modification in a
multiuser environment. This protection is the result of
the cooperative
efforts of all trusted components of the P2J environment.
Although in
theory there should be no truly untrusted
components, not all components of an application can be equally
trustworthy. Even though it is impossible to insulate them from
each
other, some layering of the code and internal checks are made to
minimize the consequences of a potentially malfunctioning component.
Participants
A P2J code typically runs as one or more multi threaded
servers,
listening on predefined ports for incoming TLS connections from remote
processes. The components of P2J concerned with security run on
the
server side. They are the Security Manager, resource plugins and
transactions. A directory service is a separate component of the
server which provides
the Security Manager with access to security relevant data
storage.
Remote processes may be either P2J utilities (called
P2J
applications or simply
applications),
which do not represent
any
particular user
or
clients that the
users may use to authenticate
themselves and then perform some transactions. Remote
processes may
make TLS
connections to the P2J servers using a known certificate or anonymously.
A server may have permanent daemon threads, transient threads, one or
more listening threads and a pool of worker
threads. The worker threads serve incoming requests on behalf
of
connected and authenticated users.
Definitions
The security model is based on these concepts:
- active entities in an application (threads) represent security subjects, which may execute
various actions on resources;
- an object called a security
context internally represents security subjects;
- objects called resources
represent the important pieces of data or points in application
logic that have to provide controllable access;
- objects called Access Control
Lists or ACLs describe
how security subjects relate to resources in terms of rights;
- when an access decision is made, the current thread's security
context defines what ACLs are checked.
Security subjects are either applications or users.
Authentication is a process of
verifying that subjects are whom they claim to be.
Authentication is
not always required. In some cases a subject may remain
anonymous.
Anonymous subjects are also associated with a security context and may
have ACLs permitting some actions on some resources.
It is application or client code that decides whether or not to
authenticate a
subject with the server. The authentication process is not hard
coded
to some algorithm. A generic authentication hook can be created
and
installed with the server to perform some form of authentication.
Users can be grouped for the purpose of easy access rights
management.
A
group is a collection of
users sharing a set of ACLs. Users may participate in multiple
groups.
The security context for such users has references to all relevant
groups.
The
Security Manager is a
code
layer that manages security contexts and ACLs and cooperates with
the resource hooks when they produce access decisions.
Each resource hook is a pluggable module that encapsulates all the
knowledge
about a category of application resources. This knowledge
includes:
- resource naming scheme;
- available actions;
- details of ACL mapping;
- custom logic to build and submit ACL expressions and interpret
the evaluated result.
Requirements
The security model should allow:
- authentication of subjects based on the clients TLS certificates
when they are presented;
- anonymous TLS connections which may be authenticated later or
remain anonymous;
- a pluggable authentication hook that allows customization of the
authentication process;
- pluggable application resource modules;
- flexible expression-based access rules;
- central storage of the security related data in the enterprise
directory;
- in a multiple server configuration, ACLs may be defined as shared
between servers, specific for a server, or a mixture of both.
Security Context
A security context is a set of objects created and maintained by the
Security Manager that are associated with server threads and that
identify each thread's rights with regards to application
resources.
A security context is accessible through a ThreadLocal object and is
not
visible outside the Security Manager. The security context is
made of a
process context, a user context and group contexts. This fact is
reflected with this notation: {process, user, group}.
When a new thread is created, there is no security context associated
with it. The thread may do other work, but it cannot request
services
from the Security Manager unless a security context gets created and
assigned for it as the result of a special API call. Once
created, the
security context remains associated with the thread for its lifetime,
if no special action is taken to change that.
Process Context
Process security context represents the client process as a subject to
the Security Manager. The only method of authentication of the
client
processes is through the use of TLS certificates. There are three
possible states of the process security context:
- no process security context;
- anonymous process security context;
- certified process security contexts.
If there is no process security context, this component cannot
pass access right checks. This is the common starting point
for all
server threads.
Anonymous TLS connections create an anonymous process security
context.
Certified TLS connections create named process security contexts.
The
rights these contexts define are the function of appropriate
ACLs. Once
created, the process security context is associated with a numeric ID
and never changes.
User Context
User security context represents the user of the client process as a
subject to the Security Manager. The user has to be authenticated
with
whatever method the authentication hook provides for the user security
context to be created. Similarly, as soon as the user signs off,
his or
her user context gets deleted.
Group Context
Group security context is just a convenience. If the user is
assigned
to one or more groups, the creation of the user security context
triggers the creation of the group security context for every group the
user is assigned to. So, in general, the group context is a set
of zero
or more objects, one per group. For convenience, this document
always
refers to an existing group context as long as there is a user
context.
Using Security Context
All server threads start with no security contexts. Any security
related API call, besides the call that creates the context, fails
immediately if called from a thread without a security context.
As any access to the controllable application resources is done only
after access rights check, the application should call one of
the available check methods that the resource type plugin provides,
passing
the resource name and (optionally) access mode. The resource type
plugin
in turn calls the Security Manager and asks the first ACL to be read.
The Security Manager obtains the security context for the calling
thread from the ThreadLocal object. Besides the case where no
security
context exists and the Security Manager returns a failure immediately,
the following cases are possible:
- process context, no user context, no group context: {process};
- no process context, user context, group context {, user, group};
- process context, user context, group context {process, user,
group}.
In the case 1, process context is the only context that can be
checked.
In the cases 2 and 3, the user context is checked and the process
context is not. In other words, the system adjusts to the user
rights
for the user logon session, and to the process rights otherwise.
Group contexts make sense only if the user context is present. In
such
a case, multiple contexts are chained for the access checks. It
is the
resource type plugin that makes the decision. If the returned ACL
allows or explicitly denies access, no further checks are made.
Otherwise, the plugin should request the next ACL in the chain.
'No
more ACLs' condition is treated as 'access denied'. This means
that all resource plugins must be written such that they deny access by
default.
Creating and
Switching Contexts
The server threads concerned with servicing of incoming requests are
either listeners or workers. When they start, they create an
initial
security context indirectly when they call some com.goldencode.p2j.net
package method depending on the nature of the thread. Ultimately,
this
resolves
into a call to the
setInitialSecurityContext()
method.
Only a known method in
the com.goldencode.p2j.net package is allowed to call a Security
Manager API that
creates the initial security contexts.
The initial security context is made of a process security context
only: {process}. This is the security context the server threads
use
when listening for incoming connections or waiting in the thread pool.
When a request comes that opens a
new
P2J session, a thread in the com.goldencode.p2j.net package is
spawned to accept the connection.
This
thread calls the Security Manager API
pushAndSwitchSecurityContext(Object)
to push its initial security
context on stack and create a new one, which corresponds to the
client's end of the connection. The client's security context is
determined by the nature of the TLS connection as follows. This
thread then initializes the networking components of the
com.goldencode.p2j.net package and starts those components. From
there, all incoming requests on that active connection are read by the
com.goldencode.p2j.net.Dispatcher which implements a thread pool that
services incoming requests.
If the TLS connection is anonymous or certified with an application
certificate, the client's security context is {process}. If the
TLS
connection is certified with an user certificate, the client's security
context is {, user, group}.
A session established under {process} security context, may
authenticate a user later and become {process, user, group} by calling
a Security Manager API.
However,
this feature is not planned for implementation this time.
Neither session established under {, user,
group} security context nor session having {process, user, group}
security context after having authenticated a user, can change
individual components of the context.
These rules limit possible security context transitions to improve
security. Briefly, they can be summarized as follows (client
security context only):
- process context cannot change once established;
- user and group context can be assigned once.
When com.goldencode.p2j.net.Dispatcher threads are done with servicing
a request, they return to the
pool and have to reset their security contexts back to the initial
state. They do it by calling a Security Manager API that pops the
client's security context off the stack and restores the initial server
security context.
This is the summary of the server <--> client security context
transitions:
- the initial server security context can be changed to a client's
security context (push and switch);
- the initial server security context can be restored when in the
client's security context (pop and restore).
There are no valid transitions between two server security
contexts. In
other words, threads cannot change their process contexts at will.
When a request comes that continues an
existing
P2J session, a thread from the pool should assume the security
context of the client associated with the session. This is the
same
action in the thread, although the Security Manager does not create a
new security context in this case.
There is a tight connection between the P2J protocol
(com.goldencode.p2j.net package) managing client
sessions and the Security Manager mapping those sessions into security
contexts.
Maintaining State Information
The P2J runtime environment and applications may need to maintain some
state information for the lifetime of the session. The old proven
method of keeping that state information on a per thread basis would
fail in P2J environment, as the Dispatcher assigns arbitrary threads
from its pool to perform elementary requests. The right place to
keep state information is the security context, because it is preserved
for the lifetime of the session.
Security Manager provides convenient methods to query, add, get and
remove pieces of state information associated with the current security
context. The state information is organized into named tokens. A token
is any kind of object. Names are arbitrary. The only requirement is not
to use names starting with "system" in applications. Names starting
with "system" are reserved for the runtime components and are protected
from the accidental use in applications.
Authentication
User Authentication
All methods of user authentication are categorized as follows:
- user TLS certificate based authentication;
- password based server side authentication;
- combined certificate and password authentication;
- distributed authentication.
User TLS certificate based
authentication means clients use user certificates
to establish TLS connections to the server instead of no certificate or
an application
certificate. There potentially is some client side code and
definitely server side code to manage this type of authentication.
The client side code is optional in the sense the server can function
with or without it. The client side prepares the user certificate
before it can be used for making the TLS connection. As the user
certificates are normally kept in the same keystore where the private
keys of their respective owners are, they
constitute pieces of important security information that must be
protected. A typical form of protection for user certificates
is
either using portable storage, symmetric password encryption or a
combination of both. The client side is responsible for
finding
the proper certificate file, prompting the user for the password and
for decryption of the certificate. Then the TLS connection is
made
and the
server authenticates the user based on the user certificate.
The P2J directory should contain a valid user account, which is
identified using the certificate as follows:
- The P2J directory is searched for a matching certificate and its
alias is selected. If no matching certificate could be found, no TLS
connection could have been established.
- The P2J directory is searched for user and process accounts that
match the alias. In general, there may be many such accounts.
Authentication fails, if there is no user account or the user and
process accounts are mixed.
- If the alias matches multiple user accounts, either the default
account should exist (name matches the alias) for the TLS based
authentication, or the user should enter one of the preselected accouts
(TLS + user ID and password).
Password based server side
authentication means clients merely send passwords
over an already established TLS connection, which provides
a secured transport. There are both client and server sides
to
manage this type of authentication. The client side code prompts
the
user for the password and sends the user ID and password over the
link.
The server side receives them, calculates a hash and compares the
result with the security data from its database.
Combined certificate and password
authentication is just the combination of the previous
two. The
user's entry in the keystore (private key and certificate) has to be
decrypted with a password first. Then,
the password is sent over the link along with the user ID for the
server side verification.
Distributed authentication
means anything more sophisticated than password based
authentication. It definitely includes both the client and server
sides, which may exchange some information over the secured TLS link
before authentication completes. This type of authentication
normally
involves some kind of biometric device or physical security
token like magnetic card reader, smart card reader etc.
In all cases, some startup code on the client drives the process and
some listener thread on the server responds to it. The flow of
control
is described below.
Client
|
Server
|
|
As part of
initialization, the
server calls getServerTransportSecurity( AuthUIHelper )
method, which returns an instance of the TransportSecurity
class, embedding KeyManager and TrustManager
objects. The KeyManager manages the server's
certificate
and private key. The TrustManager manages the
certificates of the root authorities and, possibly, individual
certificates of clients and other servers. All this information
comes
from the P2J directory.
|
|
The Listener
creates a secured
socket based on the TransportSecurity class and waits for
incoming connections. |
The Startup code
wants to create
a new session and calls the Security Manager's getClientTransportSecurity( AuthUIHelper )
method. |
|
The getClientTransportSecurity
method returns an instance of the TransportSecurity
class, embedding KeyManager and TrustManager
objects. The KeyManager manages the client's
certificate
and private key. The TrustManager
manages the certificates of the root authorities and the known
server(s). This
information comes from the bootstrap configuration. |
|
The Startup code
establishes a
secured TLS connection with the server, using the bootstrap
configuration and the TransportSecurity class. |
|
|
The Listener
accepts the
connection, gets the server side socket for it and calls the Security
Manager's authenticateLocal(SSLSocket) method. |
The Startup code
calls the
Security Manager's authenticateClient(
SSLSocket,
AuthUIHelper) method. Security Manager verifies the
server's
certificate and terminates the connection if verification fails.
The
verification procedure is identical to what is described in RFC2830, section
3.6.
|
|
Both
ends of the
connection are temporarily under control of the
Security Manager. |
Security Manager
waits for a
message from the server.
|
Security Manager
queries the
database service and finds
out the general authentication mode. The general authentication
mode
tells what type of client authentication is acceptable and, optionally,
includes some additional configuration data like class name.
Security
Manager transmits the requested authentication mode and parameters down
the link and waits for the authentication input. |
Security Manager
receives the
requested authentication
mode and, depending on the authentication mode, either performs
requested action directly or calls the authentication hook. The
authentication message is sent to the server. The client waits
for the
authentication result.
|
|
|
Security Manager
verifies the
authentication input, produces and sends the authentication result.
|
Security Manager
receives the
result.
|
|
Security
Manager
relinquishes control of the link on both ends. If authentication
has
failed, the connection is terminated.
|
|
authenticateLocal(SSLSocket)
method returns a unique context ID (which is a reference to a JVM
object) that may be used as a key to security context switches later.
|
Notes
TransportSecurity
class is a singleton.
Multiple TLS
connections are allowed.
- The
AuthUIHelper
is an interface
that hides all user interface related actions from the Security
Manager. Such actions are asking the end user for used ID and
password,
if requested by the server. This interface provides methods that
return
needed pieces of input to the Security Manager.
- The security database has a global authentication mode
configuration parameter, as well as optional per user ID overrides. The
global authentication mode parameter is used for all connections where
no client certificate is presented. Otherwise if the override exists
for the client it is used instead.
- Briefly, the authentication related exchanges between the peers
are:
Client
|
Link
|
Server
|
|
<--
|
authentication
request
|
authentication
message
|
-->
|
|
|
<--
|
authentication
result
|
A shorter form is allowed in special
cases. This shorter form is:
Client
|
Link
|
Server
|
|
<--
|
authentication
result
|
Sometimes, the server can produce a decision based on the peer
certificate. If this is the case, the authentication result is
sent
immediately. There are known cases when it happens:
- the server successfully authenticates another server by its
certificate;
- the server rejects a connection due to a bad certificate
presented by the peer;
- the server rejects an anonymous connection if they are not
allowed by configuration.
In some cases, it is desirable to be able to retry the authentication
without terminating the connection. This makes sense for the
authentication types that involve typing passwords. This feature is
fully controllable by the server. In case the server allows retries,
the authentication result indicates "retriable failure". The client has
a choice of reзуфештп the authentication messages exchange as indicated
above, using the existing conneciton or terminating the
connection.
User Authentication Hook
As can be seen from the previous sections, the user authentication is
in
general managed by two communicating pieces of code. The security
module is designed to provide user authentication as a configurable
feature. The user TLS based and the password based authentication
modes
are implemented as built-in features.
Whether the built-in authentication feature is used or not is defined
in the
authentication mode. If it is not
one of the first three choices discussed in the previous section, then
it has to be a name of a class that implements authentication.
This
class is known as an authentication hook.
An authentication hook is a class that implements the
Authenticator
interface. The built-in authentication modes implement this
interface. The basis for custom logic in the authentication
hook is
that the both sides of the connection are given a chance to run the
specified class and use the secured connection to communicate with each
other.
Authentication hooks can be dynamically downloaded from the server. The
server first reads the class data for the specified hook class. If no
such class exists, the server tries the original name concatenated with
"Client" and "Server". This is done to support single class hooks
(e.g., CustomHook.java) and separate hooks (e.g., CustomHookClient.java
and CustomHookServer.java). Separate hooks are useful when the
client side has little to nothing in common with the server side. This
is anticipated to be typically the case, since the client side is
responsible for the UI.
Process Authentication
Process authentication occurs when a remote process creates a TLS
connection to the server. If the TLS connection is established
with the
remote process' certificate verification, the identity of the
application is confirmed on the server. If there was no
certificate presented, the application remains anonymous. The
server
can be configured to reject anonymous connections or to accept them and
associate special rights with the process context.
The listener thread on the server is always prepared to get an
application's certificate, but from the TLS (industry standard)
perspective it is optional.
The overall procedure for process authentication is the same as for a
user, with
the following remarks:
- Security Manager figures out from the bootstrap configuration it
represents a process;
- the authentication message sent from the application to the
server always states it is a process authentication;
- the P2J directory should specify exactly one process account with
the matching certificate alias;
- the passwords needed to access the keystore are still available
through the
AuthUIHelper
interface.
Process authentication is based on X.509 certificates and PKCS#8
private key information.
Both are normally kept in a keystore or separate keystores which are
encrypted. Thus, to be able to get to those entries, the program
has
to present keystore password(s). There are normally two levels of
passwords. The keystore level decrypts
the whole keystore. After that, a specific entry may require its own
password.
Embedding a password into a Java program is almost as weak as listing
it in a text file. This is something to avoid if possible.
The class file format is easily read by javap or
many other tools. However, providing password based encryption
for process keystores is just an additional optional feature of process
authentication that can be handled by the generic idea of the
AuthUIHelper. This approach concentrates the decision
making about how to get passwords in one well defined place. This class
is free to implement any policy that seems appropriate - ask the user,
for instance. However, there may be cases, where ANY user interaction
is impossible (for example, in a process authentication). Note
that given a server's access to some secure storage without embedding
credentials in the program, the AuthUIHelper could be programmed to
handle such a case.
Programmatic Authentication
Programmatic authentication is intended for use with special client
applications, that require custom user interface for getting the user
ID and password. One such application is the Admin Client.
Essentially, the programmatic authentication is the user ID + password
type. However, since it is a weeker authentication compared to the
certificate based ones, the server only allows it for specially
configured user accounts.
To authenticate the session programamtically, the client application
should prepare a bootstrap configuration object and set the following
keys:
setConfigItem("security", "authentication", "type", "program");
setConfigItem("access", "subject", "id", id);
setConfigItem("access", "password", "user", pw);
The server side requirements for the account associated with the given user ID are:
- it has to be a user account
- the account definition should specify the authentication mode as AUTH_MODE_IDPW
When all these requirements are met, the programmatic authentication
succeeds. The application may use any suitable method to get the user
ID and password from the end user.
Special Case: Router/Server
Authentication
In a routed network case, one server, performing a routing function,
needs to connect to another server. In this configuration, the
originating router establishes a TLS connection with the target server
as a certified process. The
TransportSecurity
class
on
the server can be used to create outgoing TLS connections as
well. This
technique authenticates this server as a certified process on the
remote server.
Special Case: Remote User
Authentication
As the addition to the previous section, the local router as a
certified server process, may ask a remote target server to create a
security context on behalf of a local client. This cannot be
based on
the procedure described above. A different procedure exists
for
this case:
- the router where the client has been authenticated locally, calls
the Security Manager's
getIdentity
method and gets a
portable representation of the user's identity (in its simplest form,
it is a string with the user ID);
- using the existing secured connection, the router sends a message
to the
target server;
- the target server decodes the message and calls the
authenticateRemote(String
identity)
method;
- the remote Security Manager creates a security context for the
identity and returns its key;
- the key is sent back to the originating router.
This description is accurate only as far as the Security Manager is
concerned. Refer to the P2J protocol (
com.goldencode.p2j.net package)
description
for
details about routing.
Resources
As noted earlier, the notion of resource is an abstraction the Security
Manager has very little to do with. It is up to application
code
and to the resource related plugin code to agree on the rules of the
resource naming and access rights.
Abstract
Resource
Every time the application reaches an important point in the execution
where it is required by application logic or security policy to
check the user's rights before the application may proceed, we say the
application is about to check the user's access right to some
resource.
Resources exist only because applications name them and want the
Security Manager to check if the user has enough rights to continue.
This abstract and, consequently, very generic nature of
application resources dictates the need for a pluggable resource
architecture. These are the main architectural concepts of the
abstract
resources:
- abstract resources are grouped by type;
- all resources of a given type have unique names within a flat
namespace;
- resource type names are character strings;
- within a type, resources have named instances;
- every resource type has its own isolated instance namespace;
- within a type, all resource instances share the identical access
rights structure;
- The Security Manager knows as little as possible about resources;
- the implementation knowledge is encapsulated in the resource
plugins, one plugin per resource type;
- the Security Manager uses configuration data to get the list of
plugins.
The listed concepts allow for a flexible and effective implementation
of resources that can be easily extended to include new types.
Resource
Plugin
A resource plugin is an implementation of an abstract resource.
Methods of the plugin class can be called from the Security
Manager and from the application.
The
com.goldencode.p2j.security.AbstractResource
class is
a partial implementation of the abstract resource. This class
implements the
Resource
interface. The
methods from
this interface are called by the Security Manager. They are:
public void attach(SecurityManager)
passes the instance of the
SecurityManager class to the plugin
public
String getTypeName()
returns unique type name of this
category of resources
public
void registeredAs(int)
reports the assigned resource type ID
back to the plugin
public Object getLibrary()
returns an instance of a library class
for the plugin's own symbol resolution if the plugin exports any
variables or functions for use in expressions, otherwise null.
public
Description[] describeRights()
;
returns an array of Description
objects which report the details about every field of the access rights
specific to the plugin
public
Rights getRightsInstance(Field[])
returns an instance of the Rights
interface constructed using the given array of Field
objects
public
boolean isInstanceNameValid(String)
verifies if a given string is a
syntactically valid resource instance name for this category of
resources
public
boolean isRightsSetValid(Field[])
verifies if the given parameters make a
valid access rights set for this category of resources
A concrete implementation of a resource plugin has to be an extension
of the
AbstractResource
class. This guarantees that
anything that has to be programmatically enforced, can be coded as a
final method in the base class.
At server startup, the Security Manager reads the resource plugins
configuration, instantiates the plugins in turn, and calls
getTypeName(),
getLibrary(),
registeredAs(int)
and
describeRights()
methods
to create a registry of resource types. This is how the plugins
become
known components of the security model. Other methods are
discussed
later.
The application calls those methods of the resource plugin, which are
made specifically for the access rights check. As both the plugin
and
the application know what they mean, the application can call them
directly. They are all methods of the plugin class. The
application typically passes the
resource instance name and the method itself knows what to check.
Sometimes a more generic method is implemented and both the instance
name and the requested access rights are passed as parameters.
The
interface between the plugin and the application is up to the plugin
implementors.
This is an example. A plugin called
DatabaseResource
has
a method
static boolean canSeeTable(String tableName)
which the application calls somewhere as
DatabaseResource.canSeeTable("customers").
Resource
Type Name
The resource type name is a string that uniquely identifies this type
of resources. When the Security Manager calls the
getTypeName()
method, it checks the registry for duplication. If no duplication
found, the Security Manager assigns a numeric resource type ID to this
plugin and calls its
registeredAs(int)
method. The
latter
lets the plugin know the Security Manager's assigned resource
type ID. During application calls for access rights
checks, the
plugin uses the resource type ID as a parameter for all Security
Manager method calls.
Resource
Instance Name
The application uses a resource instance name as a convention with the
resource plugin. This name is used during an access rights check
procedure to associate an
ACL with
the request. It is up to the plugin implementors to introduce
instances
of the resources and name them.
Resource Access Rights
Resource plugins encapsulate all knowledge about what subjects can do
with the resources (actions). The exposed part of this
knowledge is a
set of methods that the application can call. The hidden part of
this
knowledge is the encoded access rights. The rules of this
encoding are
shared between all instances of the resource. In other words, the
data structure of access rights is associated with a
resource type rather than with a specific instance.
For the external world, the access rights are visible as a sequence of
fields in an
ACL. Neither
the number nor the interpretation of the contents of those fields are
known to the Security Manager upfront. To deal with it, the
Security
Manager expects the resource plugin to provide:
- a
describeRights()
method, which returns an array
of Description
objects;
- a class that implements the
Rights
interface and
hides the implementation of rights;
- a
getRightsInstance()
method, which creates
instances of Rights
objects from an array of field
objects.
The Security Manager gets a reference to the
Rights
interface no matter what plugin it calls, so it can handle the access
rights uniformly.
The
Rights
interface does not contain any methods.
The
implementing class, however, is something specific to the resource
plugin, and may implement methods as it seems appropriate to the plugin
implementors.
Primitive Directory Service Data
Types
As it is the Directory Service that manages storage of security
relevant configuration information, the rest of P2J relies upon the
set of primitive data types which the Directory Service can
process.
The
following is a full list of primitive data types used in the Security
Manager:
- integer; corresponds to Java's
int
type;
- boolean; corresponds to Java's
boolean
type;
- bitfield; a set of bits that can be set independently and cannot
grow dynamically (unlike the Java's
BitSet
);
- selector; a bitfield where only one bit can be set at a time (all
bits are mutually exclusive);
- string; corresponds to Java's
String
type;
- bytearray; corresponds to Java's
byte[]
type;
- date; like the Java's
Date
type with the time
portion ignored and set to 0;
- time; like the Java's
Date
type with the date
portion ignored and set to 0 (January 1, 1970, 00:00:00 GMT).
Rights Field Description
Plugins have to provide enough information about the fields of their
access
rights so that:
- access rights can be stored in the P2J directory and retrieved
upon request;
- access rights can be edited by administrators.
The way of describing the fields of access rights is through the use of
the
Description
objects. A
Description
object contains information about:
- field's primitive data type;
- whether the field is optional or mandatory;
- whether the field is variable or fixed size;
- field's size;
- field's displayable label (name);
- field's descriptive text;
- bitfield's array of bit names;
- bitfield's
BitSet
of unused bits.
Field labels must be unique within the plugin as they are used to map
fields into the P2J directory.
Field Object
Field objects are used by the Security Manager as an
intermediate step in creating instances of
Rights
.
Rights
are used in access rights checks and in P2J directory administration.
To instantiate a
Rights
object, the Security Manager uses
the array of
Description
objects. For every
Description
in the array, the corresponding instance of a field object is
created. A
field object is a ready to use
copy of an access rights field from the P2J directory. The type
of a
field object depends on the implementation of the primitive data type.
Fields marked as optional may have no associated value. The
null
reference is used in such cases.
Security Database
The security database is a collection of records that identify the
subjects
(applications, users and groups) and define what actions on abstract
resources are available for what subjects.
Subject Identification
All clients and servers are collectively known as application
subjects.
Users, user groups and application subjects are collectively known as
security subjects, or just subjects.
Subjects have identification in form of accounts. For simplicity,
all
accounts are required to have unique names. If this becomes too
much a
limitation, the internal account names may be constructed with prefixes
like 'app', 'user', 'group' followed by the external names. These
names
are externally visible subject IDs. Accounts associate subject
names
and other pieces of information like certificates and keys.
When the server starts up, the Security Manager initialization code
scans the security database for all defined subject IDs and assigns
unique temporary numeric IDs. This scan also reveals any possible
subject name duplication. The numeric IDs are valid only for the
lifespan of one server run. There is no need to preserve numeric
IDs
between server runs.
Numeric IDs are positive integers. One of the assigned IDs
represents the unidentified application subjects on the other ends of
anonymous TLS connections. This one is also called the
guest ID. There must be an
associated
guest account
defined in order to allow anonymous connections.
Access
Control Lists
The remaining part of the security database is dedicated to the access
control. The rules that define the relationship between subjects,
resources and allowed actions are formed into Access Control Lists, or
ACLs.
Because there are more than two entities involved, the ACLs may be
represented in many different ways. The representation introduced
below
does not claim to be the best ever. There cannot be just one best
representation. It is believed, though, to be a very good fit for
the
class of applications where P2J belongs.
Although technically it is stored as a tree of keys in the P2j
directory, logically it can be represented as one "file" that defines
all
ACLs. The top level structure of the file is made of sections
with
every section describing one abstract resource type:
Type "system"
ACLs for the resource type "system"
Type "database"
ACLs for the resource type "database"
...
For every configured abstract resource type, there may be zero or one
section.
Resource type sections are divided into access rights
subsections.
There must be at least one subsection, otherwise the whole abstract
resource type section should be omitted. The
rights
statement encodes the specific rights as a sequence of fields, as
described in
Resource
Access Rights.
Type "database"
Rights 4096, 1024, "true", 1
ACLs that share the rights above
Rights 4096, 1024, "false"
ACLs that share the rights above
...
Further, following the access rights is one or more groups of two
statements:
subjects
and
resources
.
The
subjects
statement lists the subject names that have the rights defined above
with respect to the resources defined here. There must be one or
more
subject names.
The last
subjects
statement for the subsection is allowed
this special form:
all_others
. This means that all
subjects not mentioned in the previous statements of this subsection
fall into this category.
The
resources
statements name instances of the resource
exactly, or
specify regular expressions for a set of matching instance names.
Due to this matching capability,
the order of definitions is very
important. The lookup happens in the order the instances
appear
in the ACL. The first matching entry wins.
Here is a sample section:
Type "database"
Rights 4096, 1024, "true", 1
subjects u1, u2, g1
resources instance "abc"
subjects u3, g2
resources instance "xyz" match "ab[0-9]"
Rights 4096, 1024, "false"
subjects all_others
resources match "ab."
Rights 2048
subjects all_others
resources instance "xyz"
As externally defined in the P2J directory, the ACLs are not suitable
for quick access. The Security Manager needs to do some
translation.
This is done at startup. The whole ACL definition is read
and
parsed. During this process:
- the abstract resource types are matched with the registered
resource plugins;
- resource instance lists are created for every resource type;
- subjects/rights lists are created for every resource instance;
- subjects are translated from the external names to the internal
IDs;
- rights are stored either as arrays of field objects (lazy
approach) or as references to
Rights
(up front approach).
This internal representation of the ACLs is optimized for the frequent
access rights checks. The lazy approach shortens the startup time
but
defers problem discovery, whereas the up front approach takes more time
at startup but guarantees validity once it is done.
The following illustrates the translation done by the Security Manager
using the sample ACL. The first step is to create triplets
{rights,
subjects, resource} keeping the order of definitions.
Rights 4096, 1024, "true", 1
subjects u1, u2, g1
instance "abc"
Rights 4096, 1024, "true", 1
subjects u3, g2
instance "xyz"
Rights 4096, 1024, "true", 1
subjects u3, g2
match "ab[0-9]"
Rights 4096, 1024, "false"
subjects all_others
match "ab."
Rights 2048
subjects all_others
instance "xyz"
The second step is to regroup the triplets under the resource
instance/match.
Instance "abc"
subjects u1, u2, g1
rights 4096, 1024, "true", 1
Instance "xyz"
subjects u3, g2
rights 4096, 1024, "true", 1
Match "ab[0-9]"
subjects u3, g2
rights 4096, 1024, "true", 1
Match "ab."
subjects all_others
rights 4096, 1024, "false"
Instance "xyz"
subjects all_others
rights 2048
The third step is to merge triplets displaying the same instance/match
("xyz" in the ACL shown).
Shared Access
Control Lists
The shared ACLs are defined in the P2J directory under a well known
path,
/security/acl
. At startup, the servers search this
path first and load all ACLs defined there, unconditionally.
Server-specific Access
Control Lists
The server-specific ACLs are defined in the P2J directory under a
different well known path,
/security/acls/server-ID
where the
server-ID
portion is variable.
At startup, the servers search this path for a node with the
server-ID
name matching the
server's ID. If such a node exists in the directory, all ACLs defined
under it are loaded, too.
Arbitrary Account
Extensions
The subject account structure is predefined and cannot change easily.
However, there may be a need to associate some extra piece of
information with the subject account, which is application-specific.
The Arbitrary account extension feature covers this need.
An application may attach a named instance of a typed variable to an
account. The supported types are:
- integer
- boolean
- string
- bytes
These types correspond to the standard directory object classes.
Applications are allowed to set and get the extension data by its name
and by subject ID, within the extent of rights granted by the directory
resource ACLs.
To use this facility, please see the
setExt<type>() and
getExt<type>() methods in
SecurityManager (for example, SecurityManager.setExtInteger()).
These methods read/write the backing values in the
/security/<server_id>/users/<userid>
container in the directory (which is the same container used to store
the standard P2J account-specific security configuration).
Access
Rights Check
The access rights are checked when and where the application wants them
to be. So, everything starts with an application call.
Application Calls
Application calls directly into a resource plugin to check access
rights. It does not have to specify the subject, because the
Security
Manager figures out the subjects involved from the security context of
the calling thread, but that happens later. It only specifies:
- the resource type, implicitly, through calling a method of a
particular resource plugin;
- the resource name, explicitly, as a parameter to the call;
- the action, sometimes called requested rights, implicitly, by
calling a specific method, or explicitly, by calling a generic method
and providing a parameter.
It is important to note, that
the security model does not provide an access rights check for third
parties. That would be the case if a security subject X
wanted
to check if a security subject Y could do some action on some
resource.
It does not mean, however, that this support cannot be implemented, if
required.
This is an example. A resource plugin called
DatabaseResource
provides a method
static boolean canSeeTable(String tableName)
.
The application calls it from somewhere as
DatabaseResource.canSeeTable("customers")
.
The reference to
DatabaseResource
implicitly specifies
the resource type. The resource name is given explicitly as
"customers"
.
The action is specified implicitly through the method name
canSeeTable
.
The same hypothetical plugin could have provided another method like
checkTableAccess(String
tableName, int accessMode)
where the requested rights would have
been explicitly coded as a method parameter.
The plugin implementation decides about the interfaces.
Typically, a
single call to
static getInstance()
method is used to get
a reference to the plugin instance, which is a singleton, and then call
instance methods using that reference.
Security
Manager and Resource Plugin Interaction
The first link in the chain is the called method in the resource
plugin. It should normally collect all the needed pieces of
information
and further call a shared private worker method to do the rest.
These
pieces of information are:
- resource type ID obtained from Security Manager;
- resource instance name;
- requested rights.
The worker method calls some methods of the Security Manager to do its
job in a predefined order.
Those
calls throw a security exception if the calling thread has no security
context.
First of all, it checks to see if the Security Manager has a
cached decision for the case, by
calling the
getCachedDecision(int resourceId, string
instanceName, int mode)
method of the Security Manager.
The
mode
parameter is a value that should consistently map all possible
requested rights into integers. This call returns either
null
or a
Boolean
. The
null
simply means
there
is no cached decision, otherwise the
Boolean
conveys what
has to be returned immediately.
The cached decision can be safely ignored should the plugin decide
so.
If it is honored, the check is done and the boolean result is returned
to the application.
The next step is to
initiate the ACL
search by calling the
openRightsSearch(
int
resourceId, string instanceName, int mode
)
method.
This call makes the Security Manager discard any existing cache entry
for the case, look up the security database and create a temporary list
of references to
Rights
objects and insert the list
into the list of open searches together with the search parameters
{resourceId,
instanceName, mode}
. This call returns an integer which
can be
considered the search handle. The
Rights
objects
selected
to the list are the result of applying the security context check
policy to the ACLs. The number of the objects and their order
complies
with the policy, as described in
Using
Security Context.
The
next step is a loop, where
the plugin gets the next
Rights
object from the list,
interacts with it and then either breaks out of the loop or continues
until the list is empty. The plugin has to be prepared to receive
no
Rights
objects at all as a valid case.
To get the next
Rights
object from the list, the plugin
calls the
getNextRights(int handle)
method which returns
either
Rights
or
null
. Once an
instance of the
Rights
object is obtained, the plugin is
free to utilize any custom interface that may exist to get the rights
evaluation done. It is up to the plugin to decide where, when and
if to
break out of the loop or continue checks. One particular case is
worth
mentioning. If the plugin decides to provide a so called
negative permission or
veto, this is how it can be
implemented. A negative permission means the access is explicitly
denied and no other checks are to be made. The plugin simply
breaks out
of the loop to implement it.
Once done with the loop (no matter how), the plugin is required to
close the ACL search. This
action has
two effects. By calling the
closeRightsSearch(int handle,
boolean
decision, boolean cache)
method, the plugin tells the
Security Manager to do the necessary cleanup and, at the same
time it posts the final decision and whether the Security Manager
should cache the result or not. The Security Manager
destroys the
temporary list of the
Rights
object and removes the
associated item from the open searches list. If caching is
requested,
it turns this item into a cache entry.
The decision whether to cache or not is left to the plugin. Some
resources may implement rights checks that depend on
external parameters which the Security Manager has no clue
about.
A typical case is the time of day dependency. Caching makes
perfect
sense in case of no external dependencies. In general,
if the result of the check depends only on the security subject,
resource
instance name and requested rights - it is a good candidate for
caching. If such a check is made frequently, then caching is
almost a
must.
Please note that it is a valid situation to have some initial access
rights check initiate another check internally. In other words,
nested security access checks are completely valid. The Security
Manager
is prepared to handle such situations. A good example is a
hierarchical resource, where an original check from the application may
cause the plugin to issue other checks for parent resources in a
hierarchy.
Using
Expressions in Access Rights
Using expressions in access rights is a way of having a highly
customizable access rights check procedure. The power of
expressions
comes from their ability to refer to various pieces of information,
referred to as variables, and even custom logic, referred to as
functions. There are two major category of variables: those
provided by
the Security Manager and those that come from the plugin.
Functions
are
provided by Security Manager and may also be provided by resource
plugins.
The names for variables and functions are scoped to avoid
clashes.
These are the rules:
- plugin scopes never clash between themselves;
- clashes are possible only between the Security Manager and a
plugin;
- Security Manager's variables are likely to be used more often and
they are well known;
- plugin's variables are added to the scope at the time of the
plugin registration; if there are any name clashes, the plugin's
symbols are registered last and override the clashing Security
Manager's symbols
The Security Manager provides variables that refer to:
- all forms of the current date;
- all forms of the time of day;
- fields of the current session/security context.
The Security Manager provides functions that:
- get the assigned groups by index;
- retrieve the values from P2J directory keys.
Both lists may grow in the future.
The Security Manager offers the use of expressions through an
API. It
is up to the plugins to decide whether they implement expressions or
not.
Expressions may be used in access rights in one of the following
ways:
- A single static expression can be built for the whole resource
type. The instances of rights only supply the variable parts of
the
expression (expression variables). In this case, the expression
is not
encoded into every instance of rights, it is implied.
- Every instance of rights comes with a fully encoded expression or
its part. A complete expression has to be built for every
instance of
rights.
- A single static expression can be built and used most of the
time. However, some instances of rights may come with a different
expression. This is the mixture of the first two cases.
There is an
implied expression which can be overridden with a specific one in some
cases, depending on the encoded rights.
The Security Manager architecture allows all these cases. The use
of an
expression is a two step process. The first step is the
expression
compilation, which is relatively expensive work but which is only done
once per unique expression. The second
step is
expression evaluation, given instances of the variables.
Due to
the expression engine implementation logic, there is no penalty in
recompiling an expression as long as the string representation of
the expression and the associated symbol resolver class do not change.
With this in mind, the three expression use patterns listed above
translate into the following coding patterns:
- The string representation of the expression is built one time and
saved at startup (plugin registration time). At access rights
check
time, this string is used to compile the expression and to evaluate
it.
The expression compilation binds the instance of the
Rights
with the expression so that the symbol resolver obtains the values of
variables from that instance of Rights
.
- The string representation of the expression is built at the
access rights check time, using the contents of the
Rights
.
It is compiled and evaluated immediately.
- The string representation of the default expression is built one
time and saved
at startup (the plugin registration time). At access rights check
time,
the plugin checks the fields of the
Rights
object and
decides whether the default expression is applicable. Either the
default or a newly built expression is compiled and evaluated.
The plugin implementors should strive to design the access rights check
procedures to use static expressions (if any) for resources with a
high frequency of checks.
Although the expression engine classes are public and can be used
directly from the plugin code, it is not recommended as the Security
Manager's pool of variables will not be available. This is the
implementation plan for the expressions in a resource plugin:
- The plugin's class implements an inner class which is the symbol
library. Only one instance of this class will be required. It can be
created either from the plugin constructor or in the
getLibrary()
method.
- The exported variables may be of the type long, boolean or
String. Every variable is implemented as a method named
getVariableName
or isVariableName
without
parameters, where VariableName
matches the variable name and is
case-insensitive. The type of the returned value of the method
determines the type of the variable.
- The exported functions may be of the type long, boolean or
String. Every function is implemented as a method named
getFunctionName
or isFunctionName
with
parameters, where FunctionName
matches
the function name and is case-insensitive. The type of the returned
value of the method determines the type of the function.
- The access rights check code calls one of the
compute()
or evaluate()
method of the Security Manager passing it an instance of Rights
that supplies the backing data.
- Security Manager calls
associate(Rights)
method of
the plugin to make a temporary association of the Rights
with the library. The plugin stores the reference into a thread local
object to avoid multithreading problems. The final implementation of
this method is provided in the AbstractResource base class.
- Expression engine calls library methods whenever necessary and
those use the associated
Rights
object to produce values
for variables and functions. Methods can use getRights()
call
to get to the Rights
object.
- Security Manager calls
disassociate()
method of the
plugin to break the temporary association of the Rights
with the library. The plugin nullifies the reference to help let that
instance go. The final implementation of this method is provided
in the AbstractResource base class.
- The result of the expression evaluation is used in decision
making.
Hierarchical Resources
Some resources naturally group into hierarchies like trees. A
well
known example is the UNIX-like filesystem. With regards to access
rights checking, this natural hierarchy may help simplify the rights
definition by utilizing the concept of
inheritance, also called
propagation. Although there
may be
various interpretations of the concept, the regular convention is to
say that a node or a leaf in the tree inherits an attribute (the access
rights in this case) from its parent node if no attribute is attached
to this node or leaf explicitly. It does not matter whether the
parent
node has an explicitly attached attribute or inherits it in turn.
This concept simplifies the ACLs definition because the vast majority
of nodes and leaves in the tree can be described with just a few
generic inheritable rules and exceptions described individually.
The Security Manager design makes it possible. Again, it is up to
a
resource plugin to introduce a hierarchical relationship between
instances of the resource type. There are no changes needed to
the ACLs
nor to the access rights design. The only thing that is different for a
hierarchical resource, is the logic of the access rights check.
Let's assume there is an instance named "/a/b/c" with no ACL defined,
and another instance "/a/b" with the rights defined as X. The
application issues a check like canUpdate("/a/b/c"). The Security
Manager returns an empty list of ACLs as the result of a call to the
openRightsSearch
.
Now the resource plugin can issue an internal check for the parent
resource: "/a/b". This check returns the rights X, so the only
thing
that is left to do is to use X as if it was returned for "/a/b/c".
Builtin Resource Type "system"
This abstract resource allows applications to provide controllable
access to the P2J system. As of time of this writing, here are
the following well known instances of this resource type:
logon
context
change
shutdown
debug
The
logon
instance represents the abstract ability to log
onto the P2J
system. Authentication procedure checks the subject's access
rights on
the
logon
instance and only allows logon if the check
succeeds.
This
resource instance allows for a very flexible control over the logon
availability.
The
context
instance helps in auditing of the context
switch category of the security relevant events. See
Auditing for details.
The
change
instance controls the access to the P2J
directory editing. Only those subjects having access to this instance,
can successfully open an editing batch.
The
shutdown
instance controls application access to the
programmatic server shutdown feature.
The
debug
instance controls application access to the
current debug level variable. The higher the variable value, the more
verbose debug output is produced. See
Debugging
for details.
Other instances of the
system
resource type may
be
added later.
The
system
resource comes with a simple structure of
access rights,
made of just one field. Here comes the description.
Field's primitive data type |
string
|
Is it optional or mandatory? |
mandatory
|
Is it variable or fixed size? |
variable
|
Field's size |
unlimited
|
Field's displayable label |
"check"
|
Field's descriptive text |
an arbitrary logical expression
that, if evaluated to "true", allows access
|
Bitfield's array of bit names |
n/a
|
Bitfield's BitSet
of unused bits |
n/a
|
Auditing
Auditing is a feature that allows retrospective analysis of the
security relevant events. Such events are:
- security context creation and deletion;
- security context switch;
- access rights check attempted as {resource type, resource
instance name, requested access};
- access rights check results.
Auditing is implemented as a simple log of formatted records of several
types. Due to a potentially high volume of records, a filtering
mechanism exists that only logs records based on the following criteria:
- events caused by a subject;
- events related to a specific resource:
- as {resource type};
- as {resource type, instance name};
- as {resource type, requested access};
- as {resource type, instance name, requested access};
- granted accesses or rejected accesses.
A special instance of the
system
resource named
context
is introduced to allow security context related events to be
selectively audited like any other resource.
Multiple filters of the same type are always combined using logical OR
operation, like in {subject1, subject2} or in {resource type1,
{resource type2, instance name2}} creating log records for all listed
events.
Multiple filter types may be combined either using OR or AND
operation.
Sometimes it is useful to log only records for subject1 about all
rejected accesses (AND operation). Other times, it is desirable
to log
accesses to a specific resource instance and, at the same time, all
rejected accesses to any resource (OR operation).
It may be possible to extend
filtering in the future by applying the expression engine.
In
order
to avoid performance degradation due to auditing, regular
expressions are not allowed in the resource instance names.
The audit log files are defined in the P2J directory as a collection of
these attributes:
- pattern for path and names of the log files
/security/audit/logfile
- size of each log file
/security/audit/logsize
- number of log files which form a ring
/security/audit/logcount
The entire audit subsystem is off by default. To enable auditing, the
/security/audit/enabled
value should be set to
true.
Configuration
Directory Service
Interface
Directory Service is the way P2J applications implement persistent
storage for the majority of configuration information. Only some
minor
portion of the configuration information is kept separately.
Generally,
this is the information needed to figure out how to get in touch with a
server (client side) or how to initialize the Directory Service (server
side), frequently called the bootstrap configuration.
Directory Service is a
package that runs on P2J servers. It
exposes a
set of APIs as public methods through its front end. The front
end is
how the rest of P2J perceives the P2J directory. The
implementation is encapsulated inside the Directory Service package and
is of no interest to the rest of P2J environment.
Security Manager Directory
Hierarchy
The following depicts all P2J directory objects maintained by the
Security Manager. Every node has attribute specification suitable
for
identifying the object and the object class in parenthesis.
/security
(container) |
single object |
config
(container)
|
single object |
resource-plugins
(strings)
|
single object |
auth-mode
(authmode)
|
single object |
accounts
(container)
|
single object |
users
(container)
|
single object |
userID
(user)
|
one object per user name |
account
extensions
|
one or more user account
extension objects
|
groups
(container)
|
single object |
groupID
(group)
|
one object per group name |
processes
(container)
|
single object |
processID
(process)
|
one object per server or
application name |
account
extensions
|
one or more process account
extension objects |
certificates (container)
|
single object |
peers
(container)
|
single object
|
alias
(bytes)
|
one object per certificate owner
name (alias) |
CAs
(container)
|
single object
|
alias
(bytes)
|
one object per CA certificate
owner name
(alias) |
audit (container)
|
single object |
enabled (boolean)
|
single object; the value set to
true enables audit
|
logfile (string)
|
single object; pattern for log
file names
|
logsize (integer)
|
single object; size of log file
in K
|
logcount (integer)
|
singleobject; number of log files
|
filterMode (booleanOption)
|
single object |
subjects (strings)
|
single object |
resources (container)
|
single object |
(auditResource)
|
one object per auditable
resource instance |
decisions (auditDecision)
|
single object |
acl
(container)
|
single object |
resource
type (container)
|
one object per resource type name |
seqno
(resourceRights)
|
one object per Rights line in
ACL, ordered by seq |
seqno
(strings)
|
one object per list of subjects,
ordered by seq
|
seqno
(binding)
|
one object per resource
instance, ordered by seq |
acls
(container)
|
single object |
server-ID
(container)
|
optional; one object per server
needing specific ACLs
|
resource
type (container)
|
one object per Rights line in
ACL, ordered by seq |
seqno
(resourceRights)
|
one object per list of subjects,
ordered by seq |
seqno
(binding)
|
one object per resource
instance, ordered by seq |
holidays
(dates)
|
single object with one value per
holiday
|
Notes
- Well known object names are in bold.
- Names in italics mean
an arbitrary name or, most likely, multiple names.
- Aliases are short unique names attached to certificates.
- The server's private key is not kept in the directory. It is
provided through the bootstrap configuration. The server's certificate,
on the other hand, should be made available through both the bootstrap
configuration (to be able to connect to a LDAP server using secure TLS
connection) and the directory (see the next note).
- All client, process and server certificates are kept under
"/security/certificates/peers" branch. They cannot be self-signed.
- All certification authorities' certificates are kept under
"/security/certificates/CAs" branch. If some of them are not
self-signed, the whole
certification chains should be made available there, including
the final self-signed root CA certificates, or else the the certificate
validity check fails.
- All servers in a multi server configuration share a single P2J
directory. Every server has to be identified through its
bootstrap
configuration, so that a matching "/security/accounts/processes/..."
object can be found. Servers with a master attribute having a
value
of
true, are allowed to edit the directory.
- Storing certificates separate from their user/process objects
allowes for a better security control.
- All known groups to which users belong, are enumerated in the
user
object.
Non-existing groups are deleted from the user definitions at startup of
the master server.
- User accounts are kept under "/security/accounts/users". One of
the accounts is selected for the session according to the rules
described in User
Authentication. If that account has no password attribute value,
the access is allowed without presenting a password.
- "/security/audit/logfile" specifies path and naming pattern for
the audit log files. The pattern definition may include some variables
that are substituted with their values at runtime. See the full
description in java.util.logging package, FileHandler class. Briefly,
these are:
- "/" the local pathname separator
- "%t" the system temporary directory
- "%h" the value of the "user.home" system property
- "%g" the generation number to distinguish rotated logs
- "%u" a unique number to resolve conflicts
- "%%" translates to a single percent sign "%"
- "/security/audit/filterMode" object specifies OR or AND operation
if multiple filters are defined.
- "/security/audit/subjects" object lists subject IDs to
audit. An
empty (omitted) set means no filtering by subject.
- "/security/audit/resources" is a container. If it's empty,
no
filtering by resource instance is in effect. Otherwise, objects
of the
auditResource class put there determine audit filtering by resource
instance.
- An instance of auditResource creates a filter that filters by
resource type, then, optionally, by one or more instance names, and
then, optionally, by one or more requested rights.
- resourceRights is a
pseudo-dynamic directory object class. resource
portion is a variable portion of the class name, taken from
the resource type name. Unlike for the other classes, the
directory class definition of resourceRights is
generated
internally by the Security Manager at startup using the
Definitions
objects. See
Directory
Service document for samples
(systemRights and directoryRights).
- seqno specifies an
integer attribute which helps maintain
the ordering of definitions, which is important for correct
interpretation of assigned rights.
- binding directory
object class is predefined. See the Directory Service document
for details.
- "/security/holidays" is a list of dates which have to be
considered holidays. The "holiday" variable when used in expressions is
evaluated based on this object. As holidays vary significantly between
countries, states and even organizations, this is the only feasible way
of specifying them accurately.
- Currently, the account extension objects are kept either under
"/security/accounts/users" or under "/security/accounts/processes", but
the location is not guaranteed. They are of one of the mandatory named
value classes.
- Authentication retries are controlled by the "retries" attribute
of the "/security/config/authMode" object. A positive number indicates
the maximum number of retries. Zero indicates no retries allowed. -1
indicates unlimited retries.
Bootstrap Configuration
The P2J directory contains most of the configuration information for
servers. However, there is a need to specify some minimal
configuration
information that would allow a server to get to the P2J
directory. This
information is what is called the bootstrap configuration.
Note that clients/applications do not have access to the P2J directory
at all. For them, any piece of information they need to connect
to the
server is part of the bootstrap configuration.
This document does not discuss how the bootstrap configuration is
organized or where it can be found. Also, only the security model
related portion of it is considered here.
Client/Application
The following security related configuration information is required on
clients/applications:
- a trust store of some sort that holds all X.509 certificates of the known servers
and the Certification Authorities (CAs);
- trust store alias of the server
managing this client/application, to identify its X.509 certificate;
- a key store of some sort that holds the application and/or the client X.509 certificates and their related
PKCS#8 private keys;
- process authentication flag
vs user authentication; if this flag indicates "process", the following
item is required as well;
- key store alias of the
application (only needed for "process" authentication mode).
Passwords and user alias that are required to access proper X.590
certificates and PKCS#8 private key are obtainable using the
AuthUIHelper
interface.
Server
The following security related configuration information is required on
servers:
- a key store of some sort that holds the server's X.509 certificate and the related
PKCS#8 private key;
- key store alias of the
server;
- the server ID.
It is the object name that has to be found under the directory path
"/security/accounts/processes".
Manageability
A P2J system must be able to run in 24x7 mode. That means
restarts cannot be used as a way of refreshing the security
database.
The
P2J directory must be editable as applications run. This
poses
some challenges:
- without caching, an edit in progress may be seen as partial
update and, therefore, cause problems;
- with caching, edited attributes remain unchanged in cache;
- more than one master server may want to edit the P2J directory.
These challenges have to be addressed by devising a methodology of
directory updates.
Editing Security Database
The visibility of partial updates is a more serious problem to crack
than caching. For this reason, the Security Manager implements a
cache,
which is also a performance booster, and a batch update strategy, which
works as follows:
- Editing security database is done using Directory Service's batch
editing feature. When opening or closing a batch, Directory Service
calls the Security Manager so the latter can keep track of the events;
- when the batch is complete, it is applied to the P2j directory
(the following internal steps are taken by the Directory Service, see
details here):
- "/security/lock" (lock)
object creation is attempted with the value attribute set to the server
ID of this server;
- the lock creation either succeeds or fails;
- if it succeeds, the server has locked the directory for
update;
- a backup copy of all objects/attributes that are about to
be changed is made;
- a boolean attribute is set to indicate the backup is
available;
- batched changes are applied to the directory objects;
- the lock object is deleted from the directory; exit 0
- if it fails, the existing lock is inspected to see if it was
created by this server;
- if it was created by a different server, the directory is
not available for editing; exit 2
- if it was created by this server, there was a system crash
and the directory may need a back out:
- the "backup available" attribute is checked next;
- if it is set, then the system crashed while applying
changes and definitely needs back out:
- back out is made using the existing backup;
- the lock object is deleted from the directory; exit 1
- if it is not set, the system crashed while making
backup;
- no back out is necessary;
- the lock object is deleted from the directory; exit 1
- Security Manager inspects the exit code; exit 0 means batched
changes are successfully applied, 2 means
retry later; 1 means retry, but the batch is lost.
- if exit code is 0, a transient thread is created that builds a
new copy of the security database cache while the current copy remains
in use;
- if a new copy is successfully created, a single synchronized
method call replaces the current cache with the new copy and lets the
old one be garbage collected.
The procedure can be enhanced to make a backup of the batch as
well.
Note, that an empty batch works like a refresh signal and the Security
Manager simply reloads the cache with the data from the directory.
Thread Synchronization
As multiple threads will be accessing the security database cache
simultaneously, including the transient refresh thread, care should be
taken to synchronize accesses and to minimize performance impact.
There is a simple method of doing this. All accesses to the cache
are
made through a synchronized method that returns a reference to the key
cache object. Using that reference does not require further
synchronization because all accesses are read only. Only the
transient
refresh thread needs to be synchronized to make the reference
substitution an atomic operation.
Debugging
Support for debugging is implemented through the multiple points in the
code where controllable messages can be produced on output. Every debug
message has a numeric level code assigned to it. The meanings of the
level codes are:
- 0 - warnings only
- 1 - adds statistics or summary messages
- 2 - adds lists
- 3 - adds detailed traces (like in authentication)
- 4 - adds security sensitive data (like passwords)
The list may grow in the future.
Error messages are internally implemented using the same approach with
the level value of -1.
Messages are printed to the System.err stream. The current debug level,
an internal variable, determines what appears on output. A level value
X allows all messages having debug level Y to appear on output if Y
<= X.
Setting the current debug level is a controllable action. A special
instance of the
system
resource named
debug
is used for access rights check. Some messages may appear before
any user can get a chance to log on, like those related to the P2J
directory reading and interpretation. In this case, the default debug
level is set soon after the server starts up, based on the server's own
account permissions.
API Summary
Interfaces
AuthUIHelper
This interface defines methods that hide the end user interaction from
the Security Manager code in a flexible way. An instance of this
interface should be created before a code may call the
getClientTransportSecurity
(
AuthUIHelper
)
or
getServerTransportSecurity
(
AuthUIHelper
)
methods. The methods of the interface should provide
pieces of
information like user ID and various passwords.
Method Signature
|
Description
|
public
char[] getKeyStorePassword();
|
Returns the password that
unlocks the key store.
|
public
char[] getTrustStorePassword(); |
Returns the
password that
unlocks the trust store. |
public
char[] getAliasPassword(String alias);
|
Returns the password that
unlocks a specific key entry identified by alias.
|
public String
getUserId(); |
Returns a string that represents
the identification of the user.
|
public char[]
getUserPassword();
|
Returns the user's password.
|
Authenticator
This interface is implemented by the built in
authorization modes and authentication hooks.
Method Signature
|
Description
|
public
byte[] clientAuthHook(AuthUIHelper auth, String parameters);
|
Implements client side custom
authorization logic. Returns a byte array to be transmitted to
the
server as authorization input. May use the given instance of AuthUIHelper
to get passwords, and custom parameters. |
public
String serverAuthHook(byte[] auth, String parameters);
|
Implements server side custom
authorization logic. Accepts the byte array produced by the
client side
authorization hook as the authentication input, and custom
parameters.
Returns a user ID (positive authentication) or null
(failed to authenticate). |
public void
clientFinalize();
|
Finalizes any resources
allocated during authentication by the client.
|
Rights
This interface has no methods. It must be implemented by a
class
from a resource plugin package.
Resource
This interface defines the resource plugin registration
procedure.
Method Signature
|
Description
|
public
void attach(SecurityManager);
|
Notifies the plugin about the
instance of the Security Manager to communicate with.
|
public
String getTypeName() ;
|
Returns the plugin resource type
as a string.
|
public Object
getLibrary(); |
Returns an instance of a library
class for the plugin's own symbol
resolution if the plugin exports any variables or functions for use in
expressions, otherwise null. |
public void
registeredAs(int); |
Notifies the plugin that the
resource type has been assigned a numeric value.
|
public Description[]
describeRights() ; |
Returns an array of
descriptions, one object per the plugin's access rights item.
|
public Rights
getRightsInstance(Object[]); |
Instantiates a plugin's class
that implements the Rights interface, using the array of
objects representing a set of access rights fields. Returns the
reference to the interface.
|
public boolean
isInstanceNameValid(String); |
Checks whether a given string is
a syntactically valid resource name for this resource type.
|
public boolean
isRightsSetValid(Object[]); |
Checks whether a given array of
objects representing a set of access rights fields is acceptable.
|
Classes
AbstractResource
This class is an abstract class that provides a partial implementation
of the
Resource
interface. The resource plugins
should
extend this base class instead of implementing the
Resource
interface directly.
TransportSecurity
This class provides the functionality of JSSE's KeyManager and
TrustManager classes without exposing those to the outside world.
Method Signature
|
Description
|
public
void attach(SSLContext) ;
|
Initializes the given SSLContext
object so that the latter uses the key manager and the trust manager
embedded into this TransportSecurity class.
|
SecurityManager
This class is the core of the Security Manager implementation.
For
convenience, methods of this class are grouped by function category.
Initialization Control Methods
Method Signature
|
Description
|
public static SecurityManager
createInstance(BootstrapConfig); |
Creates and returns an
instance of
SecurityManager class based on a given BootstrapConfig
object
|
public static SecurityManager
getInstance();
|
Returns an instance of
SecurityManager class.
|
public
TransportSecurity getClientTransportSecurity ( AuthUIHelper ); |
Creates an initialized instance
of the
TransportSecurity class by request
from the client startup code. |
public
TransportSecurity getServerTransportSecurity ( AuthUIHelper );
|
Creates an initialized instance
of the
TransportSecurity class by request
from the server startup code. |
Authentication and Session Control Methods
Method Signature
|
Description
|
public
boolean authenticateClient(
SSLSocket,
AuthUIHelper) ; |
Restricted. Access is
limited to a specific calling class/method only.
Triggers the user authentication on the client. Returns the
authentication result.
|
public
Object authenticateLocal(SSLSocket) ; |
Restricted. Access is
limited to a specific calling class/method only.
Triggers the user authentication on the server. Returns the
authentication result as an object reference (positive) or null
(negative). The returned object is a key to security context
switching. |
public
String getUserId (
) ; |
Returns the effective subject ID
from the current security context. It is the user ID if a user is
logged on, otherwise the process ID.
This method is in support of the PROGRESS' USERID() builtin function.
|
public
String getIdentity ( Object ) ; |
Restricted. Access is
limited to a specific calling class/method only.
Returns the subject identification using the result of authentication
as the key.
|
public
Object authenticateRemote(String) ; |
Restricted. Access is
limited to a specific calling class/method only.
Registers the given user as authenticated. Returns the
authentication
result as an object reference. The returned object is a key to
security
context switching. |
public
void terminateSession(Object); |
Restricted. Access is
limited to a specific calling class/method only.
Signals the TLS connection termination. If the session's security
context has the use count of 0, the security context is deleted
immediately. Otherwise, it's marked as "Pending Termination".
|
Security Context Control Methods
Method Signature
|
Description
|
public
void setInitialSecurityContext(); |
Restricted. Access is
limited to a specific calling class/method only.
Server threads call this method at startup. The calling thread
has to
have no security context.
|
public
void pushAndSwitchSecurityContext(Object); |
Server threads call this method
to temporarily switch to a user context to serve the associated
request. The calling thread has to have the initial security
context.
If the target security context is marked as "Pending Termination", an
exception is thrown as a result. Otherwise, the use count for
the
target security context is incremented by 1.
|
public
void popAndRestoreSecurityContext(); |
Server threads call this method
to restore their initial security context when they are done with the
user request. The calling thread has to have its security context
switched to a user context prior to this call.
The use count for the current security context is decremented by
1. If
it becomes 0 and the security context is marked as "Pending
Termination", it is deleted immediately.
|
State Information Control Methods
Method Signature
|
Description
|
public
boolean addToken(String name, Object token); |
Restricted. Token
names starting with "system" are of restricted use.
Token is saved in a storage associated with the current security
context under the given name.
|
public
boolean removeToken(String name); |
Restricted. Token
names starting with "system" are of restricted use.
The named token is peranently removed from the current security
context. |
public
boolean hasToken(String name) ; |
Restricted. Token
names starting with "system" are of restricted use.
Checks whether the named token is in the current security context.
|
public
Object getToken(String name);
|
Restricted. Token
names starting with "system" are of restricted use.
Gets the named token from the current security context.
|
Resource Plugin Support Methods
Method Signature
|
Description
|
public AbstractResource
getPluginInstance(String resourceTypeName);
|
Searches abstract resource
registry for a plugin that is responsible for the resources of the
specified type and returns its instance. Application calls this method
to get the instance of a plugin.
|
public
Boolean getCachedDecision(int resourceId, String
instanceName, int mode);
|
Returns a Boolean
with the decision or null if not available.
Plugins call this method to check whether a cached access check
decision is available. Cached access check decisions are
identified by
triplets of {resourceId, instanceName, mode} exactly as they were
specified when the decision was taken.
|
public
int openRightsSearch( int
resourceId, String instanceName, int mode ) ;
|
Returns a search handle.
Plugins
call this method to initiate the access rights search for the specified
resource instance and requested rights.
|
public Rights
getNextRights(int handle); |
Returns an instance of Rights
interface to be used next in the access rights check loop.
Plugins cast Rights to their own classes and use their
custom
methods to perform the check.
|
public void
closeRightsSearch(int handle, boolean
decision, boolean cache); |
Plugins call this method to
trigger cleanup of all temporary objects related to the search and
notify the Security manager about the decision just made and whether it
has to be cached or not.
|
public synchronized
Double compute(int resourceId, Rights rights, String expr) ; |
Computes an arithmetic
expression using the backing data from the specified instance of Rights .
|
public synchronized
Boolean evaluate(int resourceId, Rights rights, String
expr) ; |
Evaluates a logical expression
using the backing data from the specified instance of Rights .
|
Batch Editing Methods
These APIs is a means of controlling the editing
session from the Directory Service.
They are listed in the table below.
Method Signature
|
Description
|
public
boolean openBatch() ;
|
Restricted. Access is
limited to a specific calling class/method only.
Returns true
if an editing batch has been opened. Fails if another batch is
open for
the calling thread.
|
public
boolean isEditing() ;
|
Restricted. Access is
limited to a specific calling class/method only.
Returns true
if an editing batch is currently open.
|
public boolean
closeBatch(boolean disposition); |
Restricted. Access is
limited to a specific calling class/method only.
Closes the currently open
editing batch. The disposition parameter tells what to do with
the
closed batch:
true means apply changes to the directory;
false means discard the batch.
Returns true
if the requested disposition has been successfully applied.
|
Account Extension Access Methods
Method Signature
|
Description
|
public Integer
getExtInteger(String extName);
|
Finds the specified integer
account extension for the current user and returns the value or null.
|
public
Boolean getExtBoolean(String extName);
|
Finds the specified boolean
account extension for the current user and returns the value or null.
|
public
String getExtString(String extName); |
Finds the specified string
account extension for the current user and returns the value or null.
|
public
byte[] getExtBytes(String extName); |
Finds the specified bytearray
account extension for the current user and returns the value or null.
|
public Integer
getExtInteger(String extName, String subjectId); |
Finds the specified integer
account extension for the specified user and returns the value or null.
|
public
Boolean getExtBoolean(String extName, String subjectId); |
Finds the specified boolean
account extension for the specified user and returns the value or null.
|
public
String getExtString(String extName, String subjectId); |
Finds the specified string
account extension for the specified user and returns the value or null. |
public byte[]
getExtBytes(String extName, String subjectId); |
Finds the specified bytearray
account extension for the specified user and returns the value or null. |
public boolean
setExtInteger(String extName, int value);
|
Sets the specified integer
account extension for the current user.
|
public
boolean setExtBoolean(String extName, boolean value);
|
Sets the specified boolean
account extension for the current user.
|
public
boolean setExtString(String extName, String value); |
Sets the specified string
account extension for the current user. |
public boolean
setExtBytes(String extName, byte[] value); |
Sets the specified bytearray
account extension for the current user. |
public boolean
setExtInteger(String extName, String subjectId, int value); |
Sets the specified integer
account extension for the specified user. |
public
boolean setExtBoolean(String extName, String subjectId, boolean value); |
Sets the specified boolean
account extension for the specified user. |
public boolean
setExtString(String extName, String subjectId, String value); |
Sets the specified string
account extension for the specified user. |
public boolean
setExtBytes(String extName, String subjectId, byte[] value); |
Sets the specified bytearray
account extension for the specified user. |
Security Manager Variables
and Functions
Variables
The Security Manager creates a pool of special variables that helps in
creating flexible and powerful expressions as a way of specifying
access rights. The following table lists all Security Manager's
variables.
Category
|
Variable
|
Description
|
Session
|
Various
values taken from the current session environment.
|
|
String userid
|
User identity
|
|
String groupid
|
First or only group identity or
empty string
|
|
String appid
|
Application identity
|
|
String groups
|
Number of groups this user
account has been assigned to
|
|
int peerip
|
Peer's IP Address
|
|
int serverip
|
Server's IP Address
|
|
String peername
|
Peer's hostname
|
|
String servername
|
Server's hostname
|
|
boolean secureConnection |
true if the socket is SSL/TLS based, false if an insecure socket is in use for this session. |
|
int peernode
|
Peer's P2J node address
|
|
int servernode
|
Server's P2J node address
|
Calendar/Date
|
Various
forms of the current date
|
|
String date
|
Current date as in "YYYY/MM/DD",
suitable for comparisons
|
|
int year
|
Current year like 2005
|
|
int month
|
Current month 1-12
|
|
int dayofmonth
|
Current day of the month 1-31
|
|
int dayofyear
|
Current day of the year 1-365
|
|
int dayofepoch
|
Current day of the epoch, 0 =
Jan 1, 1970
|
|
int weekday
|
Current day of the week, sun=0,
mon=1 etc.
|
|
int weeknum
|
Current week number 1-52
|
|
boolean holiday
|
Today is a holiday (true) or a
workday (false). The implementation relies upon the P2J directory
object /security/holidays which lists all holidays
|
Time of day
|
Various
forms of the time of day
|
|
String time
|
Current time of day as in
"HH:MM:SS" from "00:00:00" to "23:59:59"
|
|
int hourampm
|
Current hour 1-12
|
|
int hour
|
Current hour 0-23
|
|
int minute
|
Current minute 0-59
|
|
int second
|
Current second 0-59
|
|
boolean AM
|
Current time is AM (true)
|
|
boolean PM
|
Current time is PM (true)
|
|
int minSinceMidnight |
Current minute since midnight
0-1439
|
|
int secSinceMidnight |
Current second since midnight
0-86399
|
Miscellaneous |
|
|
int debuglevel
|
requested debug level or -1;
useful in expressions controlling access to the debugging feature (debug
instance of the system resource)
|
Functions
All said about variables is true for the user defined functions.
The
Security Manager currently exports the following functions:
Category
|
Function
|
Description
|
Miscellaneous
|
|
|
String group(int n) |
Returns the name of the nth group
the user has been assigned to
|
Directory Access
|
Functions
querying various directory keys. The
returned type is the type of the directory key.
|
|
String dirs(String oid, String
attr)
|
Queries the first value of a P2J
attribute which
specifies a string |
|
String dirs(String oid, String
attr, int i) |
Queries the specified value of a
P2J attribute which
specifies a string |
|
long dirl(String oid, String
attr) |
Queries the first value of a P2J
key which
specifies a long integer
|
|
long dirl(String oid, String
attr, int i) |
Queries the specified value of a
P2J attribute which
specifies a long integer
|
|
boolean dirb(String oid, String
attr)
|
Queries the first value of a P2J
key which
specifies a boolean
|
|
boolean dirb(String oid, String
attr, int i) |
Queries the specified value of a
P2J attribute which
specifies a boolean
|
Appendix A. Security Utilities
The security package offers a few command line utilities:
- subjects - to batch edit the subjects list in ACLs
- migrate - to migrate the old styled ACLs to the new style
- johndoe - to validate subjects in the ACLs
- resource - to validate resource names in the ACLs
- encode - to encode passwords stored in another attribute as a plain text
WARNING! All these utilities change the specified directory file.
Please make it a rule to create a backup copy of the file before
applying any of the utilities.
Using subjects
Use
subjects to scan the specified ACLs in a directory file, select ACLs by
an account and apply deletions and/or additions to the subjects list. A
typical use of this utility may sound like this: provide access rights
that a subject X owns, to the subject Y.
How to run subjects
java com.goldencode.p2j.security.SecurityAdmin subjectsParameters
dir-file dir-path quoted-selector quoted-deletions quoted-additions
The
dir-file gets scanned started from the
dir-path node, which should specify an ACL section. All encountered ACL containers are then inspected. If
quoted-selector is
given, it has to be a subject ID. The ACLs are selected if this subject
ID makes part of the subjects list. Selection can be bypassed if
the
quoted-selector is specified as an empty string in double quotes.
The selected ACLs (or all if no selector given) are edited next. The
quoted-deletions parameter
specifies zero, one or more subject IDs that have to be removed from
the ACLs. An empty double quoted string instructs the utility not to
delete. Multiple space separated subject IDs can be specified in the
double quotes.
The
quoted-additions parameter works in a similar way. It specifes zero, one or more subject IDs that are to be added to the ACLs.
Examples
The following command will list all existing ACLs for the
system resource in the
test-directory.xml file:
java com.goldencode.p2j.security.SecurityAdmin subjects test-directory.xml /security/acl/system "" "" ""
The following command will list all existing ACLs for the
system resource and account
admins in the
test-directory.xml file:
java com.goldencode.p2j.security.SecurityAdmin subjects test-directory.xml /security/acl/system admins "" ""
The following command will add
admin2 subject ID to all ACLs for the
system resource where the
admin1 account is used
in the
test-directory.xml file:
java com.goldencode.p2j.security.SecurityAdmin subjects test-directory.xml /security/acl/system admin1 "" admin2
The following command will replace
admin2 subject ID with
admin1 and
admin3 in all ACLs for the
system resource
in the
test-directory.xml file:
java com.goldencode.p2j.security.SecurityAdmin subjects test-directory.xml /security/acl/system admin2 admin2 "admin1 admin3"
Using migrate
Use migrate to change the ACL coding style to the new style. It may be required for the offline use of an older directory.
How to run migrate
java com.goldencode.p2j.security.SecurityAdmin migrateParameters
dir-file dir-path resource-plugin
The
dir-path should specify an ACL section of the directory file
dir-file. The
resource-plugin parameter gives the class name of the resource plugin that manages this type of the resources.
Example
To migrate the net resource ACLs in a section of the test-directory.xml:
java
com.goldencode.p2j.security.SecurityAdmin migrate test-directory.xml
/security/acls/second/net com.goldencode.p2j.net.NetResource Using johndoe
Use
johndoe to analyze and validate the subjects lists for the whole ACL
section of the directory file. Validation means a subject ID refers to
a defined user, group or process account.
How to run johndoe
java com.goldencode.p2j.security.SecurityAdmin johndoeParameters
dir-file dir-path resource-plugin
The resource-plugin parameter gives the class name of the resource plugin that manages this type of the resources.
Using resource
Use
resource to analyze and validate the resource instance names for
the whole ACL section of the directory file. Validation means
resource instance names are verified by the resource plugin.
How to run resource
java com.goldencode.p2j.security.SecurityAdmin resurceParameters
dir-file dir-path resource-plugin
The resource-plugin parameter gives the class name of the resource plugin that manages this type of the resources.
Using encode
Use
encode to convert the plain text passwords stored in the user account's
some other attributes and add password attribute with the hashes.
How to run encode
java com.goldencode.p2j.security.SecurityAdmin encodeParameters
dir-file input-attr
The input-attr
parameter specifies where to get the plain text password from. It has
to be some string attribute of the user directory class. The encoded
password will be placed in the password attribute.
Copyright (c) 2004-2011, Golden Code
Development Corporation.
ALL RIGHTS RESERVED. Use is subject to license terms.