Pluggable authorization

This section describes the capability of SecureTransport Server to use custom authorization plug-ins for executing custom authorization logic and provides instructions on how to implement and configure such a plug-in.

When installing or upgrading SecureTransport 5.4 and later, an additional folder is created for the deployment of the Custom Authorization plug-ins – ‘${FILEDRIVEHOME}/plugins/authorization’.

The customer must upload a JAR file in the above directory for every Authorization plug-in that is implemented and configured in SecureTransport. The JAR file contains all the information required for the custom authorization, including the specific configuration metadata.

The SecureTransport Pluggable Authorization SPI is a set of Java interfaces and services used to create, configure, and register custom authorizers in a SecureTransport Server. It is exposed from SecureTransport 5.4 onwards.

The SPI allows customer-specific custom plug-ins to be implemented, plugged into SecureTransport Server and used as a built-in SecureTransport authorizer. The customer authorizers will be applied for end users.

Note Only one authorization plug-in can be enabled at a time with SecureTransport version 5.4 or later. Enabling more than one authorization plug-in will result in errors.

SecureTransport currently supports the following types of custom authorization:

  • Upload a file
  • Download a file
  • List content of a directory
  • Change permissions (file or directory)
  • Rename a file
  • Delete a file
  • Create a directory
  • Delete a directory
  • Change the working directory

All of the above operations are defined by the CustomAuthorizer interface from the Authorization SPI.

Additionally, one more operation is supported - file filtering operation defined by CustomFileFIlter interface from the Authorization SPI.

Create a plug-in metadata file

A META-INF/MANIFEST.MF file must be created in the plug-in’s JAR file. This metadata file is used by SecureTransport to register and use the Custom Authorization implementation as a regular SecureTransport internal Authorizer.

It can contain the following properties:

Property Value  
Authorizer-Class The full name of the class that implements the CustomAuthorizer interface. Required
Custom-File-Filter-Class The full name of the class that implements the CustomFileFilter interface. Requires CustomAuthorizer interface implementation in order to be used. Optional
Plugin-Configuration-Class The full name of the class that implements the PluginConfiguration interface. Optional
Plugin-Label Name of the custom plug-in. Required

If your Custom Authorization implementation depends on third party libraries, you have two implementation approaches:

  1. You can use a fat JAR file that stores your classes and the classes from all dependent JARs into one single JAR file.
  2. Add a Class-Path attribute to the MANIFEST.MF file, in which you must enumerate your dependent JARs separated by spaces or tabs. SecureTransport will add the enumerated libraries to the Custom Authorization implementation class path. See https://docs.oracle.com/javase/tutorial/deployment/jar/downman.html
  3. Note When you are deploying your Plug-in Authorization implementation, you must only include third party dependencies, used by your implementation. You must not add APIs provided run-time by SecureTransport. This includes the SecureTransport Pluggable Authorization SPI, Plug-in Services API and JSR-330 (javax.inject) API.
      

Authorization SPI versioning

Each Authorization SPI is versioned. Тhe initial SPI release is version 1.0. Every version extends the previous one and adds additional content. The SPI methods and classes added in a specific version have the @since annotation in their Javadoc documentation.

To declare what SPI version is used in the Authorization implementation, place the Plugin-Info.yaml file inside the Meta-Inf directory. This file contains the plug-in information, similar to the MANIFEST.MF file in Authorization SPI version 1.0. The description schema used in the yaml file is:

'<SPI version>':
  <property>: <value>
  <property>: <value>
  ...................
'<SPI version>':
  <property>: <value>
  <property>: <value>
  ...................

The convention for the indentation used is two spaces for each block.

The Plugin-Info.yaml file is introduced in Authorization SPI version 1.1. The old MANIFEST.MF file (if any) is associated with version 1.0 and is with lower priority when there is a Plugin-Info.yaml file.

If the version is not specified, the default one (1.0) will be used.

Only the methods and classes available in the specified version can be used.

The current SPI version is 1.3.

All previous Authorization SPI versions must be compatible with the latest SecureTransport version, unless stated otherwise.

Implement a CustomAuthorizer

To support any authorization operation, a plug-in must implement CustomAuthorizer interface. Create a class that implements the CustomAuthorizer interface. It must have a public default constructor and implement the declared methods which are the authorization operations.

All methods accept a DataEnvironment as a parameter and must return AuthorizationResult.

DataEnvironment is an interface that defines the current SecureTransport data for a single authorization operation. It consists of:

  • AccountDataEnv
    • account.attributes
    • account.deliveryMethod
    • account.disabled
    • account.email
    • account.enrollment
    • account.implicitEnrollment
    • account.htmlTemplate
    • account.id
    • account.name
    • account.notes
    • account.phone
    • account.type
    • account.homeDir
    • account.buzinessUnitName
    • account.userData
      • user.loginName
      • user.type
      • user.uid
      • user.gid
      • user.className
      • user.nativeUserName
  • SessionDataEnv
    • session.protocol
    • session.workDir
    • session.workDirFull
    • session.remoteAddress
    • session.remoteHost
    • session.streamingClient
    • session.isSSL
    • session.isPull
    • session.routingInitiatedBy
    • session.userLoginType
    • session.edgeId
    • session.edgeHostAddress
  • FlowDataEnv
    • flow.attributes
  • HttpDataEnv
    • http.headers
  • TransferDataEnv
    • transfer.targetDir
    • transfer.targetFull
    • transfer.target
    • transfer.targetDirFull
    • transfer.transferredBytes
    • transfer.startTime
    • transfer.xferType
    • transfer.umask
    • transfer.parentCycleId
    • transfer.coreId
    • transfer.targetRenameTo
    • transfer.targetRenameFrom
  • AuthenticationDataEnv
    • authn.ldapData
      • ldap.domainId
      • ldap.domainName
      • ldap.dn
      • ldap.authByEmail
      • ldap.uid
      • ldap.gid
      • ldap.homeDir
      • ldap.shell
      • ldap.userType
      • ldap.sysUser
      • ldap.attributes
    • authn.pluginData
      • plugin.uid
      • plugin.gid
      • plugin.homeDir
      • plugin.pluginName
      • plugin.userName
      • plugin.email
      • plugin.attributes
    • authn.ssoData
      • sso.uid
      • sso.gid
      • sso.email
      • sso.idpld
      • sso.tenant
      • sso.userName
      • sso.homeDir
      • sso.attributes

AuthorizationResult is an interface that defines the result an authorization operation must return. It provides the following properties:

  • Exit code
    • AUTHORIZATION_CONTINUE – in case of successful authorization or in case of default behavior
    • AUTHORIZATION_DENY – in case of unsuccessful authorization
  • Message: Human - readable information about the executed authorization operation
Note Make sure that you will not return null as an AuthorizationResult. In that case the authorization action will be denied.

Put your authorization logic inside and return appropriate result for the methods you want to implement.

If you don’t need authorization of particular operations, you can simply implement them to return a value of AuthorizationResult.ExitCode.AUTHORIZATION_CONTINUE.

Your custom authorizer implementation will be able to use two SecureTransport services – LoggingService and StfsService. Both of them can be used by injecting them as class members using the standard java injection annotation (see the example below). The logging service provides logging capabilities specified by your administrator. The STFS service provides a way to read the basic filesystem attributes that are stored for a files transferred by SecureTransport.

For more information about the possible SecureTransport-specific file attributes, please refer to StSpecificFileAttributes interface in securetransport-program-api.

Example of a CustomAuthorizer implementation

Below is the example of how you can implement the CustomAuthorizer interface.

import javax.inject.Inject;

public class SimpleCustomAuthorizer implements CustomAuthorizer {

@Inject

private LoggingService LOG;

private boolean isAccountEligible(AccountType type) {

return type == AccountType.VIRTUAL || type == AccountType.TEMPLATE;

}

private AuthorizationResult getResultInstance(final boolean accountEligible) {

return new AuthorizationResult() {

@Override

public ExitCode getExitCode() {

return accountEligible==true ? ExitCode.AUTHORIZATION_CONTINUE:ExitCode.AUTHORIZATION_DENY;

}

@Override

public String getMessage() {

return null;

}

};

}

@Override

public AuthorizationResult authorizeUpload(DataEnvironment dataEnvironment) {

LOG.info(“Start authorization process for UPLOAD operation.”);

final boolean accountEligible = isAccountEligible(dataEnvironment.getAccountDataEnv().getType());

return getResultInstance(accountEligible);

}

@Override

public AuthorizationResult authorizeDownload(DataEnvironment dataEnvironment) {

LOG.info(“Start authorization process for DOWNLOAD operation.”);

final boolean accountEligible = isAccountEligible(dataEnvironment.getAccountDataEnv().getType());

return getResultInstance(accountEligible);

}

@Override

public AuthorizationResult authorizeList(DataEnvironment dataEnvironment) {

LOG.info(“Start authorization process for LIST operation.”);

final boolean accountEligible = isAccountEligible(dataEnvironment.getAccountDataEnv().getType());

return getResultInstance(accountEligible);

}

@Override

public AuthorizationResult authorizeChmod(DataEnvironment dataEnvironment) {

LOG.info(“Start authorization process for CHMOD operation.”);

final boolean accountEligible = isAccountEligible(dataEnvironment.getAccountDataEnv().getType());

return getResultInstance(accountEligible);

}

@Override

public AuthorizationResult authorizeDeleteFile(DataEnvironment dataEnvironment) {

LOG.info(“Start authorization process for DELETE_FILE operation.”);

final boolean accountEligible = isAccountEligible(dataEnvironment.getAccountDataEnv().getType());

return getResultInstance(accountEligible);

}

@Override

public AuthorizationResult authorizeRenameFile(DataEnvironment dataEnvironment) {

LOG.info(“Start authorization process for RENAME_FILE operation.”);

final boolean accountEligible = isAccountEligible(dataEnvironment.getAccountDataEnv().getType());

return getResultInstance(accountEligible);

}

@Override

public AuthorizationResult authorizeCreateDirectory(DataEnvironment dataEnvironment) {

LOG.info(“Start authorization process for CREATE_DIRECTORY operation.”);

final boolean accountEligible = isAccountEligible(dataEnvironment.getAccountDataEnv().getType());

return getResultInstance(accountEligible);

}

@Override

public AuthorizationResult authorizeDeleteDirectory(DataEnvironment dataEnvironment) {

LOG.info(“Start authorization process for DELETE_DIRECTORY operation.”);

final boolean accountEligible = isAccountEligible(dataEnvironment.getAccountDataEnv().getType());

return getResultInstance(accountEligible);

}

}

Implement a CustomFileFilter

In order a plug-in to support file filter operation it must implement CustomFileFilter interface. Create a class that implements the CustomFileFilter interface. It must have a public default constructor and implement the declared method.

The method doFilter that must be implemented accepts 2 parameters – sorted set of Path objects and a DataEnvironment object. Its return type is set of Path objects which will be the filtered result.

Your file filter implementation will be able to use two SecureTransport services – LoggingService and StfsService. Both of them can be used by injecting them as class members using the standard java injection annotation (see the example below). The logging service provides logging capabilities specified by your administrator. The STFS service provides a way to read the basic filesystem metadata attributes that are stored for a files transferred by SecureTransport.

All custom file filter implementations are restricted to return no more than the target files (or part of them) which are going to be filtered. Therefore, only file deletions from the set are supported. Any experiment in adding any files to the returned set will result in a non - filtered output (like there’s no file filter implementation) in SecureTransport and an error.

Note If, for some reasons, the doFilter return null, no filter will be applied, and the original files will be listed.
Note Do not try to add files in the targetFiles. This will cause exception and the File Filter process will fail and no custom file filtering will be applied.

Example of a CustomFileFilter implementation

import javax.inject.Inject;

public class CustomFileFilterImpl implements CustomFileFilter {

/**

* Provides logging service.

*/

@Inject

private LoggingService LOG;

/**

* Provides filesystem attributes retrieval service.

*/

@Inject

private StfsService stfsService;

@Override

public SortedSet<Path> doFilter(SortedSet<Path> targetFiles, DataEnvironment dataEnvironment) {

mLogger.debug("Applying custom filter...");

AccountType accountType = dataEnvironment.getAccountDataEnv().getType();

SortedSet<Path> filteredFiles = new TreeSet<>();

if(accountType != AccountType.VIRTUAL) {

LOG.warn("Custom filter is applied only for virtual users.");

return targetFiles;

}

 

for (Path path : targetFiles) {

// Some custom filter logic.

Map<String, Object> stfsAttributes = stfsService.getAttributes(path);

if (stfsAttributes.isEmpty()) {

return;

}

StringBuilder sb = new StringBuilder("The STFS attributes for ")

.append(path.toString())

.append(" are: ");

for (Map.Entry<String, Object> stfsAttribute : stfsAttributes.entrySet()) {

sb.append("[")

.append(stfsAttribute.getKey())

.append(" : ")

.append(stfsAttribute.getValue())

.append("]");

}

logger.info(sb.toString());

}

mLogger.info("Custom filter applied successfully.");

return filteredFiles;

}

}

Performance considerations

Below you can find information on how often the authorization plug-in is called depending on some of the supported protocols:

Over FTPS:

  • For an upload operation the plug-in is called once to authorize the upload event.
  • For a download operation the plug-in is called once to authorize the download event.

Over SFTP:

  • For an upload operation the plug-in is called four times - once to authorize the upload operation and three times for the list operation.
  • For a download operation the plug-in is called once to authorize the download event.

Over HTTPS:

  • For an upload operation the plug-in is called once to authorize the upload event.
  • For a download operation the plug-in is called once to authorize the download event.
Note These results may vary depending on the client being used.

Using the SecureTransport Certificate service

SecureTransport provides a service for certificate parsing and validation against its keystore. The service is called CertificateService and can be injected in the plug-in’s CustomAuthorizer and CustomFileFilter using the following code:

@Inject

private CertificateService mCertificateService;

It declares the following methods:

  • CertificateStatus getCertificateStatus(X509Certificate certificate) – validates the certificate against the SecureTransport keystore; returns one of these enumeration values: VALID, EXPIRED, NOT_YET_VALID, UNTRUSTED.
  • KeyPair getKeyPair(String certificateAlias) – Retrieves public/private key pair from the SecureTransport keystore using certificate alias.
  • X509Certificate getCertificateByAlias(String certificateAlias) – retrieves certificate from the SecureTransport keystore using certificate alias.
  • X509Certificate getCertificateById(String certificateId) – retrieves certificate from the SecureTransport keystore using certificate’s unique identifier.
  • X509Certificate getIssuer(X509Certificate certificate) – retrieves certificate issuer from the SecureTransport keystore for given certificate.
  • List<X509Certificate> getCertificateChain(X509Certificate certificate) – retrieves certificate chain list from the SecureTransport keystore for given certificate. Can be empty, if certificate does not have a chain. The first element is the certificate itself, next element is its issuer and so on to the root certificate.
  • Collection<X509Certificate> getCertificates(String subjectDN) – retrieves certificate list from the SecureTransport keystore for given subject DN.
  • Subject getCertificateSubject(X509Certificate certificate) – extracts the domain name of the certificate’s subject into a bean of type Subject, that contains the following properties: Common name, Country, Organization, Organization Unit, Locality, State

Using the SecureTransport SSLContext service

SecureTransport provides a service for creating javax.net.ssl.SSLContext and javax.net.ssl.SSLSocketFactory objects using its internal keystore. These objects can be used for setting up secure SSL connections to other applications. This service is identical to the Certificate service exposed for Custom connectors, see SecureTransport exposed services.

The service interface is called com.axway.st.plugins.authorization.services.SslContextService and can be injected in the plug-in’s CustomAuthorizer and CustomFileFilter class, using the following code:

@Inject
private SsLContextService sslContextService;

The service methods are:

  • SSLContext createSSLContext(String certId, boolean verifyPeer) throws SecurityException;
  • SSLContext createSSLContext(String certId, boolean verifyPeer, String sslProtocol, String keyAlgorithm, String trustAlgorithm) throws SecurityException;
  • SSLSocketFactory configSSLContextFactory(SSLContext sslContext, String[] protocols, String[] cipherSuites) throws SecurityException;
  • SSLSocketFactory configSSLContextFactory(String certId, boolean verifyPeer, String sslProtocol, String keyAlgorithm, String trustAlgorithm, String[] protocols, String[] cipherSuites) throws SecurityException;

These methods have the same behavior as those from the Custom connectors’ CertificateService.

Using the SecureTransport Expression Evaluator service

You can use expression evaluator service to evaluate and validate the expressions used in custom authenticator implementation.

To use the expression evaluator service, declare а variable with @Inject annotation to the interface of the expression evaluator service provided in the API:

@Inject
private ExpressionEvaluatorService mExpressionEvaluatorService;

This service is identical to the Expression Evaluator service exposed for Custom connectors, see SecureTransport exposed services.

Enable Logger service with authorization

In order to enable logging when using the Logger service, edit the com.axway.st.plugins loggers in the tm-log4j.xml file. For example, with pluggable authorization:

<logger name="com.axway.st.plugins.authorization" additivity="false">
	<level value="info" />
	<appender-ref ref="ServerLog" /> 
</logger>

For fine-tuning debug logging, add the plug-in name in conjunction with the com.axway.st.plugins logger.

In this case, use com.axway.st.plugins.authorization.<plugin_name> as shown on the example:

<logger name="com.axway.st.plugins.authorization.authorization-plugin" additivity="false">
	<level value="debug" />
	<appender-ref ref="ServerLog" />
</logger>

Related Links