Policy Model

The current policy model has been around for a while and needs a review in light of what we have learned over the last six months, a number of issues that have come up and ongoing work at OASIS;

http://www.osoa.org/jira/browse/POLICY-15
https://issues.apache.org/jira/browse/TUSCANY-2553
http://issues.apache.org/jira/browse/TUSCANY-2499
http://www.mail-archive.com/dev%40tuscany.apache.org/msg01772.html

Summary of issues with the current arrangement

  • Better separation of policy model from assembly model
    • implementation policy sets held by component due to reuse of implementation model objects
    • OASIS are talking about moving to an external attachment model so we need flexibility to follow that move if necessary
  • "Applies to" happens right at the start of contribution and is not subject to later builder processing
  • Consistency of model style between assembly and policy models
  • Separate any policy processing from policy model
  • Consistency of policy extension model
  • definitions.xml processing should be domain wide and not restricted to a single contribution

Idealistic policy model

The make sure I understand the model I reproduced my version of it from the existing spec resources. It's slightly different from what we have at present as the code model is somewhat optimized but I want to use this as my baseline for reviewing what we have in the implementations

Policy model reading follows the assembly model in having read and resolve phases. The read phase will create dummy objects with "unresolved = true" set to represent any unresolved references. These dummy objects are replaced with the real thing at the resolve stage.

Policy and assembly model relationship

The assembly model starts life with manually configured intents and policy sets on individual assembly elements. The result of applying the intent and policySet inheritance rules and the intent and policySet matching rules leads to a final set of relationships between assembly artifacts and policySets. I started to draw these externalized from the assembly model but expect that they will resolve to placing the correct policySet QNames on appropriate assembly artifacts. These relationships drive the configuration of each binding and implementation and with wires in between.

Policy touch points

A summary of what parts of the runtime are affected by policies.

Policy Runtime

Policy implementations are provided by interceptors and by the bindings and implementations themselves. The assembly model relationships shown at the top of this diagram are as they are at the moment I believe. Need to look see if rationalization is required.

Implementation Policy

Service Policy

Reference Policy

Binding Policy

Current Policy Providers

o.a.t.s.policy.logging

Sample: samples/calculator-implementation-policies

Intent:

Tuscany specific

<intent name="logging" constrains="sca:implementation.java">
  <description>
    All messages to and from this implementation must be logged
  </description>
</intent>

Policy Set:

    <policySet name="JDKLoggingPolicy" provides="tuscany:logging" appliesTo="sca:implementation.java"
        xmlns="http://www.osoa.org/xmlns/sca/1.0">
        <tuscany:jdkLogger name="calculator">
            <logLevel>FINER</logLevel>
        </tuscany:jdkLogger>
    </policySet>

Note:

The intent is read from the policy-logging definitions file
Each component is set up with all policy provider factories
During wire creation RuntimeWireImpl adds implementation interceptors and the policy provider factories for the component look at the component/operation to see if an interceptor is required
On invoke the logging takes place

o.a.t.s.policy.security.jaas

Sample: samples/calculator-implementation-policies

Intent:

Tuscany specific

    <intent name="jaasAuthentication" constrains="sca:implementation.java">
        <description>All invocations to be authenticated</description>
    </intent>

Policy Set:

    <policySet name="JaasPolicy" provides="tuscany:jaasAuthentication" appliesTo="sca:implementation.java"
        xmlns="http://www.osoa.org/xmlns/sca/1.0">
        <tuscany:jaasAuthentication>
            <tuscany:configurationName>Calculator</tuscany:configurationName>
            <tuscany:callbackHandler>calculator.security.CalculatorCallbackHandler</tuscany:callbackHandler>
        </tuscany:jaasAuthentication>
    </policySet>

Note:

The intent is read from the policy-securitydefinitions file
Each component is set up with all policy provider factories
During wire creation RuntimeWireImpl adds implementation interceptors and the policy provider factories for the component look at the component/operation to see if an interceptor is required
On invoke the JAAS login is performed which relies on a set of Tuscany classes to authenticate the credentials returned from a callback?
The callack though has no message context from which to pull credentials?
Subject is not flowed through to the component

o.a.t.s.policy.security.ws

Sample: samples/helloworld-ws-service-secure

Intent:

SCA Defined

	<intent name="authentication" 
	 		constrains="sca:binding">
		<description>
			Specifying this intent on references requires necessary authentication information
			to be sent along with outgoing messages. Specifying this intent on service requires
			incoming messages to be authenticated
		</description>
	</intent>
	
	<intent name="confidentiality" 
	 		constrains="sca:binding">
		<description>
			Specifying this intent requires message exchanged to be encrypted
		</description>
	</intent>
	
	<intent name="integrity" 
	 		constrains="sca:binding">
		<description>
			Specifying this intent requires message exchanged to be signed
		</description>
	</intent>

Policy Set:

Example of policy set that sets up Rampart through config parameters

  <sca:policySet name="hw:wsAuthenticationPolicy"
 	provides="authentication"
 	appliesTo="sca:binding.ws"
 	>
 	<tuscany:wsConfigParam>
 		<parameter name="InflowSecurity">
 			<action>
 				<items>UsernameToken</items>
 				<passwordCallbackClass>helloworld.ServerPWCBHandler</passwordCallbackClass>
       		</action>
      	</parameter>
 	</tuscany:wsConfigParam>
 </sca:policySet>

Note:

During Axis2ServicProvider.start() the policy handlers are first identified based on the computed policy sets and then the axis configuration is modified using these policy handlers.

o.a.t.s.policy.transaction (the policy implementation part)

TBD

Policy Scenarios

Authentication - WS Security

Policy set and sample available

Authentication - User Defined

No implementation yet. User defined authentication tokens could be useful when the Tuscany user has their own single sign on framework.

1. The client application authenticates and receives a token by some means out of band to Tuscany
2. The client application sends messages to Tuscany including the authenticated token. The intent is either authentication.message or authentication.transport
3. The tuscany runtime (host, binding, policy code) allows extensible policy support to validate the token against some 3rd party identitiy system.

To make this work there are a number of moving parts that we need to think about.

Decide what format the security token is going to take. From a general Tuscany point of view we don't actually have to decide what the token will look like but we have to understand what formats are valid, of particular standards are allowed etc.

JMS
Assume authentication.message is required here
It's likely the security token will travel in a JMS message property so the JMS binding needs to be extended to be able to call out to policy providers in order to authenticate the token and retrieve the Subject. The Token/Subject should then be placed in the Tuscany message context.

WS
How is a security token to be flowed to a web service

If authentication.message is in force then this is going to rely on sessions at the SOAP level and could take a similar approach to the existing ws-security based policies to extract the token from a suitable session header. We should make sure the token is available to external (to axis) authentication code Then we need to push the token and the subject into the Tuscany message context

If authentication.transport is in force then the token needs to be extracted from the transport. We expect the container/Axis stack to do this for us initially and put it in the service context. Again we need to get it from there and push this out to the external (to axis) authentication code. Again we need to push the token and the subject into the Tuscany message context

Both JMS and WS approaches have the same pattern...

1. Perform some binding & policy specific processing to extract the token
2. Perform some deployment specific processing to authenticate the token, i.e. call the identity provider
3. Place the results of these two stages in the message context.

These steps require a closer integration between the bindings and the policy providers than we have currently.

Example of how binding.ws is configured in this scenario

Authentication - HTTP Basic

Need same capability as described in single sign on to get close to the web service binding and configure the transport (reference side) extract transport context (service side).

Definitions.xml

    <sca:policySet name="WSBasicAuthenticationPolicySet"
                   provides="authentication"
                   appliesTo="sca:reference/sca:binding.ws">
                   
        <tuscany:wsBasicAuthentication>
          <tuscany:userName>myname</tuscany:userName>
          <tuscany:password>mypassword</tuscany:password>
        </tuscany:wsBasicAuthentication>
        
     	<tuscany:wsAxisOption>
                <propertySetter name="org.apache.tuscany.sca.binding.ws.axis2.policy.authentication.basic.WSBasicAuthenticationReferencePolicyAxisOptions"/>
     		<property name="_NTLM_DIGEST_BASIC_AUTHENTICATION_"/>
                <authenticator>
                   <authScheme>Basic</authScheme>
                   <premtiveAuthentication>true</premtiveAuthentication>
                </authenticator>
          	</property>
     	</tuscany:wsAxisOption>
        
    </sca:policySet>
    
    <sca:policySet name="WSBasicAuthenticationPolicySet"
                   provides="authentication"
                   appliesTo="sca:service/sca:binding.ws">
                   
        <tuscany:wsBasicAuthentication>
          <tuscany:userName>myname</tuscany:userName>
          <tuscany:password>mypassword</tuscany:password>
        </tuscany:wsBasicAuthentication>
        
    </sca:policySet>   

Reference side interceptor:

public class WSBasicAuthenticationReferencePolicyInterceptor implements Interceptor {
    public static final QName policySetQName = new QName(Constants.SCA10_TUSCANY_NS, "wsBasicAuthentication");

    private Invoker next;
    private Operation operation;
    private PolicySet policySet = null;
    private String context;
    private WSBasicAuthenticationPolicy policy;

    public WSBasicAuthenticationReferencePolicyInterceptor(String context, Operation operation, PolicySet policySet) {
        super();
        this.operation = operation;
        this.policySet = policySet;
        this.context = context;
        init();
    }

    private void init() {
        if (policySet != null) {
            for (Object policyObject : policySet.getPolicies()){
                if (policyObject instanceof WSBasicAuthenticationPolicy){
                    policy = (WSBasicAuthenticationPolicy)policyObject;
                    break;
                }
            }
        }
    }

    public Message invoke(Message msg) {
        // could call out here to some 3rd part system to get credentials
        msg.getQoSContext().put(WSBasicAuthenticationPolicy.WS_BASIC_AUTHENTICATION_USERNAME,
                                policy.getUserName());
        msg.getQoSContext().put(WSBasicAuthenticationPolicy.WS_BASIC_AUTHENTICATION_PASSWORD,
                policy.getPassword());
        
        return getNext().invoke(msg);
    }

    public Invoker getNext() {
        return next;
    }

    public void setNext(Invoker next) {
        this.next = next;
    }
}

Service side interceptor

public class WSBasicAuthenticationServicePolicyInterceptor implements Interceptor {
    public static final QName policySetQName = new QName(Constants.SCA10_TUSCANY_NS, "wsBasicAuthentication");

    private Invoker next;
    private Operation operation;
    private PolicySet policySet = null;
    private String context;
    private WSBasicAuthenticationPolicy policy;

    public WSBasicAuthenticationServicePolicyInterceptor(String context, Operation operation, PolicySet policySet) {
        super();
        this.operation = operation;
        this.policySet = policySet;
        this.context = context;
        init();
    }

    private void init() {
        if (policySet != null) {
            for (Object policyObject : policySet.getPolicies()){
                if (policyObject instanceof WSBasicAuthenticationPolicy){
                    policy = (WSBasicAuthenticationPolicy)policyObject;
                    break;
                }
            }
        }
    }

    public Message invoke(Message msg) {
        Object[] header = msg.getHeader();
        
        Map httpHeaderProperties = (Map)Object[0];
        
        String basicAuthString = (String)httpHeaderProperties.get("Authorization");
        String decodedBasicAuthString = null;
        String username = null;
        String password = null;
        
        if (basicAuthString != null) {
            basicAuthString = basicAuthString.trim();
            
            if (basicAuthString.startsWith("Basic ")) {
                decodedBasicAuthString = new String(Base64.decode(basicAuthString.substring(6)));
            }
            
            int collonIndex = decodedBasicAuthString.indexOf(':');
            
            if (collonIndex == -1){
                username = decodedBasicAuthString;
            } else {
                username = decodedBasicAuthString.substring(0, collonIndex);
                password = decodedBasicAuthString.substring(collonIndex + 1);
            }
            
            // could call out here to some 3rd part system to do whatever you 
            // need to turn credentials into a principal            
            
            msg.getQoSContext().put(Message.QOS_CTX_SECURITY_PRINCIPAL, username);             
        }
    
        return getNext().invoke(msg);
    }

    public Invoker getNext() {
        return next;
    }

    public void setNext(Invoker next) {
        this.next = next;
    }
}
public class WSBasicAuthenticationReferencePolicyAxisOptions {
    
    public WSBasicAuthenticationReferencePolicyAxisOptions(){
    }
    
    public void setServiceOptions(ServiceClient serviceClient) {
    }
    
    public void setOperationOptions(OperationClient operationClient, Message msg) {
        
        // get security context
        String securityPrincipal = (String)msg.getQoSContext().get(Message.QOS_CTX_SECURITY_PRINCIPAL);
        String username = null;
        String password = null;
        
        // could use the security principal to look up basic auth credentials
        if (  securityPrincipal != null ) {
            // look up usename and password based on security principal
        } else {
           // take the message username and password
            username = (String)msg.getQoSContext().get(WSBasicAuthenticationPolicy.WS_BASIC_AUTHENTICATION_USERNAME);
            password = (String)msg.getQoSContext().get(WSBasicAuthenticationPolicy.WS_BASIC_AUTHENTICATION_PASSWORD);
        }
        
        if (username == null || password == null ){
            throw new ServiceRuntimeException("Basic authenication username or password is null");
        }
        
        HttpTransportProperties.Authenticator authenticator = new HttpTransportProperties.Authenticator();
        List<String> auth = new ArrayList<String>();
        auth.add(Authenticator.BASIC);
        authenticator.setAuthSchemes(auth);
        authenticator.setPreemptiveAuthentication(true);
        authenticator.setUsername(username);
        authenticator.setPassword(password);
    
        operationClient.getOptions().setProperty(HTTPConstants.AUTHENTICATE,
                                                 authenticator);
    }
    
    public void setMessageOptions(MessageContext messageContext) {
        
    }

}

Confidentiality - WS Security

Policy set and sample available

Integrity - WS Security

Policy set and sample available

  • No labels