Friday, June 26, 2009

WSO2 SOA Summer School - Security in SOA : The brain friendly edition of complex security specs

I'll be doing the webinar, WSO2 SOA Summer School: Security in SOA on 2nd of July - next Thursday.

As many businesses move ahead with SOA, security and identity management need to be made available as a service in the architecture in a consistent and reusable way across all applications.

During this webinar I will focus on implementing key security standards and identity management for SOA with regards to two emerging user centric identities: OpenID & Information Cards, and also XACML for fine-grained authorization.

If you are yet to register - do it today from here.

I hope you'll enjoy this session and learn Security concepts in a brain friendly manner.

Thursday, June 25, 2009

SOA Summer School: Scalable SOA by Samisa

Samisa will be doing a presentation on the $subject in few hours time.

Serious business applications that demand high volumes of transactions are most often based on SOAs. Clustering technologies are critical for some of these deployments to help achieve load balancing and high availability. This session digs deep into the scaling techniques that can be used in SOA deployments that demand high throughput and high reliability.

If you are yet to register - still its's open - further details here.

Monday, June 22, 2009

Extracting SAML assertions from a proxy service and adding them back to the service behind

This blog post explains how to do the intial setup with WSO2 ESB and the Identity Server to develop claim aware web services - please go through it first.

There - we get the SAML Assertion to the proxy service.

Here what we are going to do is - to extract SAML Assertions from the Security header of the incoming message and add those as a custom header to the message going to the actual service.

First, please download the org.wso2.carbon.identity.samples.saml.mediator-2.0.0.SNAPSHOT.jar from here.

Stop - the ESB if it's running already.

Copy the above jar to [ESB_HOME]\webapps\ROOT\WEB-INF\plugins\console.

Open [ESB_HOME]\webapps\ROOT\WEB-INF\eclipse\configuration\org.eclipse.equinox.simpleconfigurator\bundles.info and add the following entry to the end [one line].

org.wso2.carbon.identity.samples.saml.mediator,2.0.0.SNAPSHOT,file:plugins/org.wso2.carbon.identity.samples.saml.mediator-2.0.0.SNAPSHOT.jar,10,true

Start the ESB with following.

\> wso2server.bat -cleanCache

Now, you need to add the above mediator to the in sequence of your proxy service as a Class mediator.

Following is the synapase configuration for the in/out sequences of the proxy service.

<syn:proxy name="test" transports="https http" startOnLoad="true" trace="disable">
  <syn:target>
    <syn:inSequence>
      <syn:class name="org.wso2.carbon.identity.samples.saml.mediator.SAMLAttrMediator"/>
      <syn:header xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" name="wsse:Security" action="remove"/>
      <syn:log level="full"/>
      <syn:send/>
    </syn:inSequence>
    <syn:outSequence>
      <syn:send/>
    </syn:outSequence>
  </syn:target>
</syn:proxy>
You can clearly see Class/Header/Log & Send mediators are in the in-sequence and Send mediator in the out-sequence.

Now - the question is how do we capture the SAML attributes from the actual service.

You can call the following method from any service method to get the attributes in a HashMap.

getAttributes(MessageContext.getCurrentMessageContext());

Following is the code for getAttributes() method.

private static final String USER_ATTRIBUTES_ELEMENT = "UserAttributes";
private static final String USER_ATTRIBUTES_NS = "http://attributes.saml.axis2.org";
private static final String ATTRIBUTE = "Attribute";
private static final String ATTRIBUTE_NS = "AttributeNamespace";
private static final String ATTRIBUTE_VALUE = "AttributeValue";
public final static String SAML10_NS = "urn:oasis:names:tc:SAML:1.0:assertion";
public final static String SAML11_NS = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1";
public final static String SAML20_NS = "urn:oasis:names:tc:SAML:2.0:assertion";

public Map getAttributes(MessageContext messageContext) {
OMElement header = null;
OMElement userAttr = null;
Iterator iterator = null;
String samlNameSpace = null;

header = messageContext.getEnvelope().getHeader();
userAttr = header.getFirstChildWithName(new QName(USER_ATTRIBUTES_NS,
USER_ATTRIBUTES_ELEMENT));
iterator = userAttr.getChildrenWithName(new QName(SAML10_NS, ATTRIBUTE));

if (iterator.hasNext()) {
samlNameSpace = SAML10_NS;
} else {
iterator = userAttr.getChildrenWithName(new QName(SAML11_NS, ATTRIBUTE));
if (iterator.hasNext()) {
samlNameSpace = SAML11_NS;
} else {
iterator = userAttr.getChildrenWithName(new QName(SAML20_NS, ATTRIBUTE));
if (iterator.hasNext()) {
samlNameSpace = SAML20_NS;
}
}
}

if (samlNameSpace == null) {
// Unsupported SAML token type;
return null;
}

Map attributes;
attributes = new HashMap();

while (iterator.hasNext()) {
OMElement attr = iterator.next();
OMElement attrValElement = null;
String attributeName = null;
String attributeValue = null;
attributeName = attr.getAttributeValue(new QName(ATTRIBUTE_NS));
attrValElement = attr.getFirstChildWithName(new QName(samlNameSpace, ATTRIBUTE_VALUE));
attributeValue = attrValElement.getText();
attributes.put(attributeName, attributeValue);
}

return attributes;
}
Also make sure you import following packages as well.

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.namespace.QName;
import org.apache.axiom.om.OMElement;
import org.apache.axis2.context.MessageContext;

Friday, June 19, 2009

WSO2 Identity Server 2.0


Thursday, June 18, 2009

SOA Summer School: SOA Enterprise Architecture Patterns by Asanka

Asanka will be doing a training on the $subject in few hours time.

This session will provide in-depth knowledge on how to implement an SOA solution using the basic elements in an SOA infrastructure. There will be several enterprise SOA patterns mapped to real world and hypothetical business requirements. Implementation details will be practically explained using the WSO2 SOA platform.

If you are yet to register - still its's open - further details here.

Wednesday, June 17, 2009

Guide to write XACML policies in WSO2 Identity Server 2.0 - Part - I

This blog post explains how to write policies in XACML for WSO2 Identity Server.

A given policy has an identifier, a rule combining algorithm, a description, a target and a set of rules.

<Policy PolicyId="urn:sample:xacml:2.0:samplepolicy"
  RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable"
  xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os">

  <Description>Sample XACML Authorization Policy.</Description>

  <Target>...</Target>

  <Rule>...</Rule>

</Policy>
Since, a given Policy may contain multiple Rules, each of which may evaluate to different access control decisions, XACML needs some way of reconciling the decisions each makes.

This is done through a collection of Combining Algorithms.

Each algorithm represents a different way of combining multiple descisions evaluated through different rules into a single descision.

Following rule combining algorithms are defined in XACML 2.0.

urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:deny-overrides
urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides
urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable
urn:oasis:names:tc:xacml:1.1:rule-combining-algorithm:ordered-denyoverrides
urn:oasis:names:tc:xacml:1.1:rule-combining-algorithm:ordered-permitoverrides
when we have urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable as the rule combining alogorithm, it will pick the first rule applicable from the defined set of Rules.

Once a XACML request being received at the PDP it needs to find a policy that applies to the corresponding request.

To do this, XACML uses the element Target.

A Target is basically a set of simplified conditions for the Subject, Resource and Action that must be met for a Policy or Rule to apply to a given request.

Once a Target is defined directly under Policy element - it defines the set of conditions that must be met to pick that Policy.

<Policy PolicyId="urn:sample:xacml:2.0:samplepolicy"
  RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable"
  xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os">

  <Description>Sample XACML Authorization Policy.</Description>

  <Target>

    <Subjects>...</Subjects>
    <Resources>...</Resources>
    <Actions>...</Actions>

  </Target>

  <Rule>...</Rule>

</Policy>
To make this very clear - let's go through an example.

This policy will be picked for a request having, any Subject, any Action on Resource http://localhost:8280/services/echo/.

<Policy PolicyId="urn:sample:xacml:2.0:samplepolicy"
  RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable"
  xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os">

  <Description>Sample XACML Authorization Policy.</Description>

  <Target>

    <Subjects> <AnySubject/> </Subjects>

    <Resources>
      <Resource>
        <ResourceMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match">
        <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">http://localhost:8280/services/echo/</AttributeValue>
        <ResourceAttributeDesignator
          AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id"
          DataType="http://www.w3.org/2001/XMLSchema#string"/>
        </ResourceMatch>
      </Resource>
    </Resources>

    <Actions> <AnyAction/> </Actions>

  </Target>

  <Rule>...</Rule>

</Policy>
For the time being, let's not worry too much about <Resources/> element.

Let's see another example. Here the Target is applied to the Rule - not to the entire Policy it self.

<Policy PolicyId="urn:sample:xacml:2.0:samplepolicy"
  RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable"
  xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os">

  <Description>Sample XACML Authorization Policy.</Description>

  <Rule Effect="Permit" RuleId="primary-access-rule">

    <Target>

      <Subjects> <AnySubject/> </Subjects>

      <Resources>
        <Resource>
          <ResourceMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match">
          <AttributeValue
            DataType="http://www.w3.org/2001/XMLSchema#string">http://localhost:8280/services/echo/</AttributeValue>
          <ResourceAttributeDesignator
            AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id"
            DataType="http://www.w3.org/2001/XMLSchema#string"/>
          </ResourceMatch>
        </Resource>
      </Resources>

      <Actions> <AnyAction/> </Actions>

    </Target>

  </Rule>

</Policy>
I guess, that explains enough about <Policy/> and <Target/> elements.

Let's move to the <Rule/> element. You may recall, that I mentioned there can be multiple Rule elements per given Policy.

Also - we discussed about Rule Combining Algorithms.

Let's focus on the child elements of the Rule element.

The way the Sun XACML engine determines whether a rule is applicable to an incoming request is by evaluating the Target and optional Condition (if it exists).

These are ANDed together, and the rule's effect achieved if the ANDed value is TRUE.

You know where Rule element fits in to a XACML policy - for the purpose of explaining, I'll just take out the isolated Rule element only - so keep in mind this is always within <Policy> tags.

<Rule Effect="Permit" RuleId="primary-access-rule">

  <Target>...</Target>
  <Condition>...</Condition>

</Rule>
A policy contains one or more Rules.

Each rule has a RuleId and an Effect.

An Effect is the intended consequence of a satisfied rule, which can be either "Deny" or "Permit." This means that if the rule is deemed applicable to an incoming service request, and the rule's conditions evaluate to TRUE, then the specified effect should be enforced.

Since we already discussed about the <Target/> element under the context of <Rule/>, we'll skip it here.

Let's focus on the <Condition/> element.

A Condition is a predicate that must be satisfied for a rule to be assigned its effect.

Okay - a Target defines a set of constraints for a Rule. So - what does a Condition do? Let's differentiate Condition from Target.

While Targets are appealing, frame-like expressions, they have a constrained logic which isn't always expressive enough to narrow down whether a policy is applicable to a service request.

Hence, the need for Condition element arises. If either the policy Target or the Rule Target is not able to adequately express a constraint, a Condition can be added to a Rule.

A Condition can only present within a Rule.

If a Condition is intended to be applicable to the entire Policy, the Condition must be repeated in every Rule in that Policy.

Let's go ahead with an example.

We want to restrict users based on their attributes.

Say - for example a given user has an accessList attribute - and we want to restrict access to a given resource based on the accessList.

I'll take an isolated <Condition/> element here...

<Condition>
  <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-at-least-one-member-of">
    <SubjectAttributeDesignator AttributeId="accessList" DataType="http://www.w3.org/2001/XMLSchema#string"/>
    <Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-bag">
      <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">nurses</AttributeValue>
      <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">doctors</AttributeValue>
    </Apply>
  </Apply>
</Condition>
Let's start from the inner most <Apply/> element.

It uses the string-bag function on two attributes.

This bag function wraps a set of possible values for the attribute defined under <SubjectAttributeDesignator/> element. In this case - possible values for the attribute 'accessList' should be either 'nurses' or 'doctors'.

Now, let's look at the outer most <Apply/> element. It uses string-at-least-one-member-of function which will be applied on the results of the inner function.

In other words - final condition says - if you want to access the resource you have to be a member of 'doctors' or 'nurses'.

This will basically conclude the part-1 of this series of blog posts.

In the next blog post I would like to write about Attributes and Attribute Designators.

Sunday, June 14, 2009

Anatomy of the XACML Request

Going by an example is the easiest way to explain.

<Request xmlns="urn:oasis:names:tc:xacml:2.0:context:schema:os" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Subject>
  <Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id" DataType="http://www.w3.org/2001/XMLSchema#string">
    <AttributeValue>admin</AttributeValue>
  </Attribute>
  <Attribute AttributeId="group" DataType="http://www.w3.org/2001/XMLSchema#string">
    <AttributeValue>admin</AttributeValue>
  </Attribute>
</Subject>
<Resource>
  <Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" DataType="http://www.w3.org/2001/XMLSchema#string">
    <AttributeValue>http://localhost:8280/services/echo/echoString</AttributeValue>
  </Attribute>
</Resource>
<Action>
  <Attribute AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id" DataType="http://www.w3.org/2001/XMLSchema#string">
    <AttributeValue>read</AttributeValue>
  </Attribute>
</Action>
<Environment/>
</Request>
Let's started with breaking it down.

<Subject>

Represents the entity making the access request.

Can contain multiple <Attribute> elements.

A given Subject is identified by the attributes contained.

Each <Attribute> has two attributes. AttributeId and the DataType.

AttributeId can be a one of your own or one defined by the XACML specification.

Following are the set of special identifiers defined in XACML related to the Subject.

urn:oasis:names:tc:xacml:1.0:subject:authn-locality:dns-name
urn:oasis:names:tc:xacml:1.0:subject:authn-locality:ip-address
urn:oasis:names:tc:xacml:1.0:subject:authentication-method
urn:oasis:names:tc:xacml:1.0:subject:authentication-time
urn:oasis:names:tc:xacml:1.0:subject:key-info
urn:oasis:names:tc:xacml:1.0:subject:request-time
urn:oasis:names:tc:xacml:1.0:subject:session-start-time
urn:oasis:names:tc:xacml:1.0:subject:subject-id
urn:oasis:names:tc:xacml:1.0:subject:subject-id-qualifier
urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
urn:oasis:names:tc:xacml:1.0:subject-category:codebase
urn:oasis:names:tc:xacml:1.0:subject-category:intermediary-subject
urn:oasis:names:tc:xacml:1.0:subject-category:recipient-subject
urn:oasis:names:tc:xacml:1.0:subject-category:requesting-machine
In our example, for the first Attribute under the Subject we use the special identifier, urn:oasis:names:tc:xacml:1.0:subject:subject-id. For the next Attribute, we use our own - 'group'. This can be anything - mail,givenName,accessList or any custom attribute - which could be identified by the defined XACML policy, where this request will be evaluated against.

The next attribute defined in the <Attribute> element is the DataType. It basically defines the type of data <AttributeValue> element should contain.

Following are set of data types defiend in XACML.

http://www.w3.org/2001/XMLSchema#string
http://www.w3.org/2001/XMLSchema#boolean
http://www.w3.org/2001/XMLSchema#integer
http://www.w3.org/2001/XMLSchema#double
http://www.w3.org/2001/XMLSchema#time
http://www.w3.org/2001/XMLSchema#date
http://www.w3.org/2001/XMLSchema#dateTime
http://www.w3.org/TR/2002/WD-xquery-operators-20020816#dayTimeDuration
http://www.w3.org/TR/2002/WD-xquery-operators-20020816#yearMonthDuration
http://www.w3.org/2001/XMLSchema#anyURI
http://www.w3.org/2001/XMLSchema#hexBinary
http://www.w3.org/2001/XMLSchema#base64Binary
urn:oasis:names:tc:xacml:1.0:data-type:rfc822Name
urn:oasis:names:tc:xacml:1.0:data-type:x500Name
In our case both the Attributes are of type http://www.w3.org/2001/XMLSchema#string.

So, the bottom line is, the <Subject> element defines who wants to access.

<Resource>

This defines, the data,service or system component - which the Subject wants to access.

The <Resource> element contains one or more attributes of the resource to which the subject (or subjects) has requested access. There can be only one <Resource> per decision request.

Once again, a given <Resource> is identified by the <Attribute> child element. In our example, the Subject wants access to the Resource http://localhost:8280/services/echo/echoString.

<Action>

The <Action> element contains one or more attributes of the action that the subject (or subjects) wishes to take on the resource. There can be only one action per decision request.

Here also, a given <Action> is identified by the <Attribute> child element. In our example, the Subject wants Read access to the resource http://localhost:8280/services/echo/echoString.

<Environment>

A more complex request context may have contained some attributes not associated with the subject, the resource or the action. These would have been placed in an optional <Environment> element following the <Action> element.

Wednesday, June 10, 2009

Extending WSO2 Identity Server 2.0 to handle custom SAML assertions

WSO2 Identity Server 2.0 is a free and open source identity and entitlement management server, available to download from here...

Before reading this, you need to go through my previous blog post on 'WSO2 Identity Server + Claim aware proxy services with ESB' - available here.

Once you get the above running - all the client requests to attributes are satisfied through Identity Server's defaut implementation.

What if you need to filter attributes being sent?

What if you need to invoke an EJB to obtain custom attributes, and insert them as a SAML assertion to the returning security token?

In other words, you need to execute some custom logic in side Identity Server 2.0 to manipulate attributes being added to the returning security token... how to do this?

This blog post explains all what you need to make that happen.

First make sure - the first scenario works fine. That is, now you get the Security Token with SAML attribute assertions inserted by the Identity Server.

Now let's move to the second scenario - now we are going to override it.

Step -1

We need to write a carbon componet to insert our custom logic.

Please download the sample already written from here.

Extract the sample.aar. And run;

mvn clean install

from sample folder.[I have tested this with a clean maven repo].

Now, you can find the bundle inside, sample\org.wso2.carbon.identity.samples.attributeservice\target\org.wso2.carbon.identity.samples.attributeservice-2.0.0.SNAPSHOT.jar.

Step -2

Stop Identity Server [IS] if it's already running.

Copy org.wso2.carbon.identity.samples.attributeservice-2.0.0.SNAPSHOT.jar to [IS_HOME]\webapps\ROOT\WEB-INF\plugins\server

Open [IS_HOME]\webapps\ROOT\WEB-INF\eclipse\configuration\org.eclipse.equinox.simpleconfigurator\bundles.info and add the following entry to the end [one line].

org.wso2.carbon.identity.samples.attributeservice,2.0.0.SNAPSHOT,file:plugins/org.wso2.carbon.identity.samples.attributeservice-2.0.0.SNAPSHOT.jar,10,true

Start the IS with following.

\> wso2server.bat -DosgiConsole -cleanCache

Once got started - press <ENTER> on the console - you'll get the osgi prompt.

Type the following there - to list the available bundles.

osgi\> ss

Check the status of org.wso2.carbon.identity.samples.attributeservice bundle and get it's bundle ID [say 164]. If the status is RESOLVED then type;

osgi\> start 164

Now once again check the status with ss - it should be ACTIVE now.

Step -3

Run the same client used here before.

You'll see the 'First Name' attribute being overridden as well as a new attribute being added.

Also you'll see following line on the console.

INFO - SampleAttributeService being called

Thursday, June 4, 2009

WSO2 Identity Server + Claim aware proxy services with ESB

WSO2 Identity Server 2.0 is a free and open source identity and entitlement management server, available to download from here...

Let me first briefly explain the the use case.

1. A proxy service created in WSO2 ESB requires a security token issued by the WSO2 Identity Server for authentication.

2.At the same time, the security policy in the proxy service, specifies - it requires a given set of claim values with the security token.

<sp:RequestSecurityTokenTemplate xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey</t:KeyType>
<t:KeySize>256</t:KeySize>
<t:Claims Dialect="http://wso2.org/claims" xmlns:ic="http://schemas.xmlsoap.org/ws/2005/05/identity">
<ic:ClaimType Uri="http://wso2.org/claims/givenname" />
</t:Claims>
</sp:RequestSecurityTokenTemplate>
3. Identity Server is connected to an LDAP user store and all user attributes reside there.

4. User needs to authenticate to Identity Server, first and obtain the Security Token with claims.

5. Then user sends it to the ESB proxy service.

Let's see how we can achieve this in a step by step manner.

Step -1

Set up LDAP server. All the detail required explained here.

Step -2
Configure WSO2 Identity Server to talk to the LDAP Server and do the claim mapping. All the detail required explained here.

Step -3
Configure WSO2 Identity Server STS. The first part of this explains all the steps you need.

Before that you need to get the public certificate [wso2carbon.cert.cer] of the ESB from here.

Login to IS as an admin, and import the above certificate to wso2carbon.jks from Configure/Key Stores. [Yes - WSO2 ESB and IS use two different key stores - that's why we need to perform this step here]

Make sure, in the first step - while adding the trusted 'Endpoint Address' - select 'wso2carbon.cert' as the 'Certificate Alias'.

Also - while applying the security policy to the STS - make sure you select the group where ldap users[ldapuserole] belong to.

Step -4

Create and apply security for the proxy service. You need to follow exact steps defined here [first part] - but need to get the service security policy[service.policy.xml] from here, when you are overriding.

That's it - now let's write the client code.

You may also need to download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 5.0 from here and copy the two jar files from the extracted jce directory (local_policy.jar and US_export_policy.jar) to $JAVA_HOME/jre/lib/security. For JDK 6 it's here.

You need to do above, both at the client side as well as the server side - if you are running on two machines.

Also while running the client code, make sure bouncycastle jar is in the classpath.

You can download both sts.policy.xml and service.policy.xml files from here.

package org.apache.ws.axis2;

import java.util.Properties;

import javax.xml.namespace.QName;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.neethi.Policy;
import org.apache.neethi.PolicyEngine;
import org.apache.rahas.RahasConstants;
import org.apache.rahas.Token;
import org.apache.rahas.TokenStorage;
import org.apache.rahas.TrustUtil;
import org.apache.rahas.client.STSClient;
import org.apache.rampart.RampartMessageData;
import org.apache.rampart.policy.model.RampartConfig;
import org.apache.rampart.policy.model.CryptoConfig;
import org.apache.ws.secpolicy.Constants;
import org.opensaml.XML;

public class IdentitySTSClient {

/**
* @param args
*/

final static String RELYING_PARTY_SERVICE_EPR = "http://localhost:8280/services/echo";
final static String ESB_TRANS_EPR = "http://localhost:8280/services/test";
final static String STS_EPR = "https://localhost:9443/services/wso2carbon-sts";

/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
ConfigurationContext confContext = null;
Policy stsPolicy = null;
STSClient stsClient = null;
Policy servicePolicy = null;
Token responseToken = null;
String trustStore = null;

// You need to import the Identity Server, public certificate to this key store.
// By default it's there - if you use wso2carbon.jks from [ESB_HOME]\resources\security
trustStore = "wso2carbon.jks";
// We are accessing STS over HTTPS - so need to set trustStore parameters.
System.setProperty("javax.net.ssl.trustStore", trustStore);
System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");

// Create configuration context - you will have Rampart module engaged in the
// client.axis2.xml
confContext = ConfigurationContextFactory.createConfigurationContextFromFileSystem("repo",
"repo/conf/client.axis2.xml");

stsClient = new STSClient(confContext);

stsClient.setRstTemplate(getRSTTemplate());
stsClient.setAction(RahasConstants.WST_NS_05_02 + RahasConstants.RST_ACTION_SCT);

// This is the security policy we applied to Identity Server STS.
// You can see it by https://[IDENTITY_SERVER]/services/wso2carbon-sts?wsdl
stsPolicy = loadSTSPolicy("sts.policy.xml");

// This is the security of the relying party web service.
// This policy will accept a security token issued from Identity Server STS
servicePolicy = loadServicePolicy("service.policy.xml");

responseToken = stsClient.requestSecurityToken(servicePolicy, STS_EPR, stsPolicy,
RELYING_PARTY_SERVICE_EPR);

System.out.println(responseToken.getToken());

TokenStorage store = TrustUtil.getTokenStore(confContext);
store.add(responseToken);

ServiceClient client = new ServiceClient(confContext, null);
Options options = new Options();
options.setAction("urn:echoString");
options.setTo(new EndpointReference(RELYING_PARTY_SERVICE_EPR));
options.setProperty(org.apache.axis2.Constants.Configuration.TRANSPORT_URL, ESB_TRANS_EPR);
options.setProperty(RampartMessageData.KEY_RAMPART_POLICY, servicePolicy);
options.setProperty(RampartMessageData.KEY_CUSTOM_ISSUED_TOKEN, responseToken.getId());
client.setOptions(options);

client.engageModule("addressing");
client.engageModule("rampart");

OMElement response = client.sendReceive(getPayload("Hello world1"));
System.out.println("Response : " + response);
}

private static Policy loadSTSPolicy(String xmlPath) throws Exception {
StAXOMBuilder builder = null;
Policy policy = null;
RampartConfig rc = null;

builder = new StAXOMBuilder(xmlPath);
policy = PolicyEngine.getPolicy(builder.getDocumentElement());
rc = new RampartConfig();
// User from the LDAP user store
rc.setUser("prabath");
// You need to have password call-back class to provide the user password
rc.setPwCbClass(PWCBHandler.class.getName());
policy.addAssertion(rc);
return policy;
}

private static Policy loadServicePolicy(String xmlPath) throws Exception {
StAXOMBuilder builder = null;
Policy policy = null;
RampartConfig rc = null;
CryptoConfig sigCryptoConfig = null;
String keystore = null;
Properties merlinProp = null;
CryptoConfig encrCryptoConfig = null;

builder = new StAXOMBuilder(xmlPath);
policy = PolicyEngine.getPolicy(builder.getDocumentElement());
rc = new RampartConfig();
rc.setUser("wso2carbon");
rc.setEncryptionUser("wso2carbon");
// You need to have password call-back class to provide the user password
rc.setPwCbClass(PWCBHandler.class.getName());

keystore = "wso2carbon.jks";
merlinProp = new Properties();
merlinProp.put("org.apache.ws.security.crypto.merlin.keystore.type", "JKS");
merlinProp.put("org.apache.ws.security.crypto.merlin.file", keystore);
merlinProp.put("org.apache.ws.security.crypto.merlin.keystore.password", "wso2carbon");

sigCryptoConfig = new CryptoConfig();
sigCryptoConfig.setProvider("org.apache.ws.security.components.crypto.Merlin");
sigCryptoConfig.setProp(merlinProp);

encrCryptoConfig = new CryptoConfig();
encrCryptoConfig.setProvider("org.apache.ws.security.components.crypto.Merlin");
encrCryptoConfig.setProp(merlinProp);

rc.setSigCryptoConfig(sigCryptoConfig);
rc.setEncrCryptoConfig(encrCryptoConfig);

policy.addAssertion(rc);
return policy;
}

private static OMElement getRSTTemplate() throws Exception {
OMFactory fac = OMAbstractFactory.getOMFactory();
OMElement element = null;
OMElement elem = fac.createOMElement(Constants.RST_TEMPLATE);
TrustUtil.createTokenTypeElement(RahasConstants.VERSION_05_02, elem).setText(XML.SAML_NS);
TrustUtil.createKeyTypeElement(RahasConstants.VERSION_05_02, elem,
RahasConstants.KEY_TYPE_SYMM_KEY);
TrustUtil.createKeySizeElement(RahasConstants.VERSION_05_02, elem, 256);
element = TrustUtil.createClaims(RahasConstants.VERSION_05_02, elem,"http://wso2.org");
addClaimType(element,"http://wso2.org/claims/givenname");
return elem;
}

private static void addClaimType(OMElement parent,String uri) {
OMElement element = null;
element = parent.getOMFactory().createOMElement(new QName("http://schemas.xmlsoap.org/ws/2005/05/identity", "ClaimType", "wsid"),
parent);
element.addAttribute( parent.getOMFactory().createOMAttribute("Uri",null,uri));
}

private static OMElement getPayload(String value) {
OMFactory factory = null;
OMNamespace ns = null;
OMElement elem = null;
OMElement childElem = null;

factory = OMAbstractFactory.getOMFactory();
ns = factory.createOMNamespace("http://echo.services.core.carbon.wso2.org", "ns1");
elem = factory.createOMElement("echoString", ns);
childElem = factory.createOMElement("in", null);
childElem.setText(value);
elem.addChild(childElem);
return elem;
}
}

package org.apache.ws.axis2;

import org.apache.ws.security.WSPasswordCallback;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;

public class PWCBHandler implements CallbackHandler {

public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
WSPasswordCallback cb = (WSPasswordCallback) callbacks[0];
if ("prabath".equals(cb.getIdentifier())) {
cb.setPassword("prabath");
} else {
cb.setPassword("wso2carbon");
}
}
}

Wednesday, June 3, 2009

Connecting WSO2 Identity Server 2.0 to an LDAP based user store

WSO2 Identity Server 2.0 is a free and open source identity and entitlement management server, available to download from here...

Here, we'll be discussing all the steps required to deploy WSO2 Identity Server over an LDAP user store.

First we need to setup an LDAP server.

You may download Apache Directory Studio from here and set it up.

This explains all what you need to set it up.

1. Log in to Identity Server as an admin and go to User Manager and click on 'Add External User Store'.

2. Add your LDAP server settings and click Finish.

3. Click on 'Test Connection' to test the connectivity.

4. Click on 'External Users' and then 'Search' to list users from the external user store.

5. Go back to the previous screen and click on 'External Roles'. And then 'Add New Internal Roles'. Here we are going to create a new internal role for external users.

6. Give the new role a name.

7. Select a set of permissions.

8. Search users from the external user store and add them to the role.

9. Now - we need to map the LDAP attributes the claims read by Carbon.

10.Default claim dialect for Carbon is http://wso2.org/claims. Click on it - under 'External User Store'.

11.Click on the claims you want to change and set it's attribute name properly to the LDAP attribute.

That's and we are done. Now users from the LDAP user store can login to WSO2 Identity Server.

Setting up Apache Directory Server as the LDAP User Store

First we need to download Apache Directory Studio from here and install.

1. Start - Apache Directory Studio.

2. File --> New and select Apache DS Server

3. Window --> Show Views --> Other... select Servers

4. Right click on the 'wso2identity' server on the 'Servers' view and select 'Open Configuration'. Note that default port being set to 10389.

5. Window --> Show Views -->Connections and right click on the 'Connections' view to select 'New Connection...'.

6. Set hostname and port number of the server we just created.

7. Default password of admin is 'secret'.

8. Done. View the LDAP Browser. Now we are going to add a new user.Right click on 'ou=users' and select 'New Entry'.

9. Select 'inetOrgPerson' from left and add.

10.Set the attribute values for the user.

11.That's it - we are done.

Connecting WSO2 Identity Server to a JDBC external user store

Yumani has written a nice blog post on the subject. Available here...