Showing posts with label OSLC. Show all posts
Showing posts with label OSLC. Show all posts

Thursday, 1 October 2015

DWA headless authentication from Node.JS service

In my previous posts I was using dwaOauth.js to perform OAuth dance with DOORS Web Access. This was using DWA provided login page which required user to provide his DOORS credentials.

Then I showed you TRS feed reader which was using the same dwaOauth module. But it was very inconvenient to start reader and then manually authenticate with DWA. I would expect my server to connect to remote server itself and then just view the changes in the feed.

It's time to separate view from model and make them two separate applications.

Form authentication

With aid of superagent library one can perform form authentication with DWA and then use a agent to handle further TRS calls:
user1 = superagent.agent();
user1
  .post('https://your.dwa.server:port/dwa/j_acegi_security_check')
  .type('form')
  .send({ j_username: 'doors_user', j_password: 'password'})
  .end(function(err, res) {
    if (err != null) {
      console.log(err)
      user1 = null
    }
    else if (res.status != 200) {
      console.log(res.status)
    }
    else {
      console.log('user logged in to DWA')
      trs.TRSUpdate(user1, storeTRS);
    }
  })

Now user1 is authenticated and can be used to get data from DWA:

user1
  .get('https://your.dwa.server:port/dwa/rm/trs/changelog')
  .set('Accept', 'text/turtle')
  .buffer(true)
  .end(function(err, res) {
      // use res.text for plain text response from DWA
}

If you do not specify .buffer(true) res.text will be an empty string!

Monday, 14 September 2015

TRS in DOORS Next Generation

DOORS Next Generation has two TRS specifications implemented TRS and TRS 2.
With very small changes to the my previous posts you can read TRS1 feed from DNG.

Well if you remember my first post you will notice that my dwaOauth.js works just fine with DNG. So that little module gives you not only access to DWA protected resources but you can reuse it with DNG or other CLM servers.
Remember there were small changes done in order to make it working with most recent Node.js. You can read about those changes in my OSLC TRS listener.

DNG setup

You will need to prepare your DNG to provide TRS. Have a look at Register applications as TRS providers for the Lifecycle Query Engine in IBM Knowledge Center.

Usually DNG is ready to provide TRS feed, all you need is to assign a license to user who would be used to read it.

Also you will need to check permissions on /lqe/web/admin/permissions

Now yow are ready to rad TRS from DOORS Next Generation.

Changes in trs.js

There are very little changes in function getChangelogFromDWA
> var previous = '/rm/trs';
var previous = '/dwa/rm/trs/changelogs'

and
> if (previous == '/rm/trs') {
if (previous == '/dwa/rm/trs/changelog') {

So we just changes the starting point for triple feed.

You probably will notice the difference in TRS changelog id in DNG feed. It ses uuid and it looks something like:
urn:uuid:cdaf9dae-ad2d-4434-87a7-aea2488182d6

Thus we need to modify parseChangeLogs function parser:

async.whilst(
  function () { return rest != trs.nil && order > iLastOrder},
  // get ordered list or changes
  function (next) {
      var o = store.find(rest, trs.first, null)[0];
      if (typeof o !='undefined') {
        
        var timearr = o.object.split(':');
        if (timearr[0] == 'urn') {
            // update order
            order = parseInt(n3.Util.getLiteralValue( store.find(o.object, trs.order, null)[0].object) );
            idx++;
            //
            //  DO SOMETHING USEFUL HERE
            //
        }
        
          rest = store.find(rest, trs.rest, null)[0].object;
      } else {
          rest = trs.nil;
      }
      next();
  },
  function (err) {
    console.log("read " + idx + " changelog items");
      last(changes);
  });

DWA has change id has a useful format so one doesn't need further requests to gets some basic details of the change occurred but it is easy enough to write some callback functions getting changes from DNG. Of course one should not relay on format of the id as it is not standardized, defined nor promised not to change. 

Conclusion

Well TRS is TRS ;) with little changes to my inital TRS posts you can consume DWA, DNG or even Bugzilla TRS feed. In near future I will explain how to modify this parser for TRS 2.0.
Stay tuned!

Thursday, 30 July 2015

OSLC and DOORS 9 Hierarchy

OSLC is still evolving standard. Currently there is a version 2, which even though it has major improvements over version 1, it still lacks some definitions which could help when it comes to data representation. 

When you have a look at the OSLC Resource RequirementCollection you might notice the only relation to a OSLC Requirement is oslc_rm:uses. Described as:
"A collection uses a resource - the resource is in the requirement collection"

Thus OSLC RequirementCollection could be illustrated as an array or a table of non-related requirements.


That's not really useful when you look at the size and structure of a single DOORS 9 module used in production environment. You know a module it is not a flat list with thousands of requirements, it is more like a tree with lots of branches where each Heading or Subheading is a local root.
DOORS 9 structure is more complex.

Loosing a structure when converting DOORS 9 module to OSLC Requirements Collection might not be a problem when you want simply display its unordered contents or simply find a Requirement. However the power of DOORS comes from relations, traceability and reporting which are often key factors in many standards.

How to make a tree tree

Sadly OSLC cannot help us with requirements parenting or sorting so we need to get help elsewhere. 

If you had a look a my introductory post on OSLC DXL Services you know what to do!
Indeed all you need is a DXL Service which would return the structure for you. Yeah I know there were those perms and scripts you had to write...
Well since that post on DXL services there was DOORS 9.6.1.3 release and guess what! OSLC DXL Services got their UI. Finally! I mean DXL services perms were there for quite a long time, yet there was not much use of them.

In Database Explorer File -> OSLC you can see new item "DXL Services"
new menu flow in DOORS 9

Similarly to other configuration tools in DOORS this menu is not available for everyone.

This opens DXL Services UI which is relatively simple, yet does its job.
DXL Services UI

Hierarchy DXL script

In the picture above helloWord is the script from preview post, but what is "hierarchy"? I know I didn't put description but the function name gives some hints ;)

Code listing for hierarchy.dxl can be found here. It wasn't tested thoroughly, it might be, and should be optimized - specially for URL re-usage but that's not a part of this post.

Once you add this script to your DXL services it is ready to use.

Short summary how to get use of a DXL service:
Send a HTTP PUT request to your server with specified service name

https://yourserver:8443/dwa/rm/dxl/hierarchy

setting header to:
accept: application/rdf+xml

and content initialized with DXL services arguments (remember to encode your arguments for XML):
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:doors="http://jazz.net/doors/xmlns/prod/jazz/doors/2.0/">
  <doors:Arguments>
  <doors:arguments>https%3A%2F%2Fyourserver%3A8443%2Fdwa%2Frm%2Furn%3Arational%3A%3A1-DBID-M-MODULE_ID</doors:arguments>
  </doors:Arguments>
</rdf:RDF>


DWA should return with the structured document similar to below:
<?xml version="1.0"?>
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:doors="http://jazz.net/doors/xmlns/prod/jazz/doors/2.0/">
  <doors:DxlServiceResult rdf:about="https://yourserver:8443/dwa/rm/dxl/hierarchy">
    <doors:result rdf:parseType="Literal"><OSLCDXLService name="oslc_getHierarchy" version="1.0">
    <Module uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-M-MODULE_ID" fullName="/Folder" viewName="" />
    <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-1-MODULE_ID">
        <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-2-MODULE_ID">
            <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-8-MODULE_ID">
                <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-14-MODULE_ID">
                    <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-20-MODULE_ID">
                        <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-24-MODULE_ID">
                            <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-25-MODULE_ID"/>
                            <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-26-MODULE_ID"/>
                            <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-27-MODULE_ID"/>
                        </Object>
                    </Object>
                    <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-15-MODULE_ID"/>
                    <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-19-MODULE_ID"/>
                </Object>
                <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-9-MODULE_ID"/>
                <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-13-MODULE_ID"/>
            </Object>
        </Object>
        <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-3-MODULE_ID"/>
        <Object uri="https://yourserver:8443/dwa/rm/urn:rational::1-DBID-O-7-MODULE_ID"/>
    </Object>
</OSLCDXLService></doors:result>
  </doors:DxlServiceResult>
</rdf:RDF>

Now depending on your needs you can parse it and create DOORS 9 like view of a Module in your application.

Monday, 22 June 2015

OSLC Resource Selection

Last time I presented idea of OSLC DXL Services which can very powerful and useful.
One can write a DXL services against database, projects or modules, but providing DOORS resources links manually might be very inconvenient.
There is much easier way to select your resource.
DWA server is OSLC RM Provider and as such it provides resource selection delegated UI.
You can find more information on Delegated UI here.
So before I will continue with next more complex examples of OSLC DXL Services or more powerful Node.JS applications I will focus on simple resources selection.
In terms of OSLC you can select Requirement Collection (a Module) or single Requirement (Object/Requirement).

Protocols

There are two protocols which OSLC Consumer could use to select resouce:
  • #oslc-core-postMessage-1.0 - Indicates that the Post Message protocol is to be used
  • #oslc-core-windowName-1.0 - Indicates that the Window Name protocol is to be used
Here I will show how to use post message protocol which works with all modern browsers. OSLC specification provides UI consumer's responsibilities which should be followed:
  1. Include the Delegated UI Dialog via iframe (i.e setting iframe src to the URI of the Delegated UI Dialog) or via an embedded browser. Append the fragment identifier #oslc-core-postMessage-1.0 to the URL to indicate that Post Message is the desired protocol.
  2. Add a 'message' listener to the outer window to receive messages from the Delegated UI Dialog.
  3. Listen for window 'message' events, ignoring events from other sources or not prefixed with "oslc-response:"
  4. When message from Delegated UI Dialog indicates user completed action, free resources and handle action. 

1. Providing an iframe for Delegated UI

Let's look at very basic HTML page:
<!DOCTYPE html>
<html>
<head>
    <title>DWA OSLC Delegated UI test</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script type="text/javascript" src="/scripts/delegatedui.js"></script>
</head>

<body >
    <h1>DWA OSLC Delegated UI test</h1>
    <div>
        <button id='buttonSelect' type="button" onclick="delegatedUI.selectResource()">Select module</button>
    </div>
    <div id="OSLCMainDiv"></div>
    <div id="anotherDiv"  >
    </div>
</body>
Here I have just a button and some divs. You can find full source of /scripts/delegatedui.js here.
Button "Select Module" has following callback:

function selectResource() {
        clear();
        
        // Add 'iframe' element dynamically
        var frameName = "";
        frame = createElement("iframe", frameName);
        frame.id = "OSLCContainer";
        frame.width='400px';
        frame.height='500px';

        addEvent(window, "message", onMessageReceived);
        
        var mainDiv = document.getElementById("OSLCMainDiv");
        if (mainDiv) {
            mainDiv.appendChild(frame);
        }

        frame.src = "https://server:port/dwa/rm/calm/v2/selectRequirementCollection" + "#oslc-core-postMessage-1.0";
    }

This simply dynamically adds an iframe, registers its callback and appends "#oslc-core-postMessage-1.0" as chosen protocol.
Of course one should discover OSLC Service Provider selection dialog using Service oslc:selectionDialog property (as defined specification), but I used general DWA schema for requirement collection (a module) selection.

2. Adding message listener

We need to registering 'message' event listener as called from selectResource function:

function addEvent(obj, type, func) {
        if (obj.addEventListener) {
            obj.addEventListener(type, func, false);
        }
        else if (obj.attachEvent) {
            obj.attachEvent("on" + type, func);
        }
        else {
            obj["on" + type] = func;
        }
    } 

3. Listen to events

Once message is registered we have to listen to incoming events ignoring those not prefixed with "oslc-response:" :

 function onMessageReceived(event) {
        if (frame && event.source === frame.contentWindow) {
            var prefix = "oslc-response:";
            if (event.data.substring(0, prefix.length) === prefix) {
                done(event.data.substring(prefix.length));
            }
        }
    }


With 'done' method triggered when 'oslc-response' is found in event data:
function done(data) {
        var title = JSON.parse(data, function(key, value) {
            if (key.equals('rdf:resource')) {
                clear();
                // TODO: add you logic here
                // value contains a link to the resource
                // title is a Module name
            }
        });
    }

4.  Cleanup


Whatever cleanup you require that's the place to do it. I simply remove listener and iframe element

function clear() {
        var mainDiv = document.getElementById("OSLCMainDiv");
        if (mainDiv) {
            // clear any existing child elements
            mainDiv.innerHTML = ''; 
        }

        removeEvent(window, "message", onMessageReceived);
    }

Friday, 5 June 2015

OSLC DXL Services

One of the primary strategies of the IBM Rational division is to continually improve upon the integration capabilities of Rational and non-Rational products.
Keystone to this strategy is Open Services for Lifecycle Collaboration (OSLC). 
However a common complaint heard from providers / consumers of OSLC functionality is that there can be certain business problems that cannot be addressed because they rely on data and/or functionality that are (a) not currently available in the implemented standard or (b) considered too specialized for inclusion in the standard and therefore not planned for future versions.
Yet all is not lost and as OSLC service discovery is extensible it is possible to add services to help close such gaps. 
An excellent example of this can be seen in the introduction of OSLC DXL Services to Rational DOORS.

What are OSLC DXL Services?

Put simply they are an extension to the Rational DOORS OSLC RM V2 interface which allows users to execute Rational DOORS DXL scripts safely and securely across HTTPS.

Yes you can run DXL scripts from DWA! Well not all DXL scripts you have of course and only those registered by Administrator as OSLC DXL service but still this gives your project new possibilities and powers.

What makes DXL Script a OSLC DXL Service?

If you looked at IBM OSLC DXL Service introduction you came across helloWorld.inc:

void getHelloString(string language)
{
    string hello = null
    if ("French" == language) {
        hello = "Bonjour le monde" 
    }
    else if ("Finnish" == language) {
        hello = "Hei maailma"
    }
    else if ("Latin" == language) {
        hello = "Ave mundi" 
    }
    else {
        hello = "Hello world" 
    }

    setDxlServiceResult hello
    print hello "\n" 
}
If you are familiar with DXL you can spot setDxlServiceResult PERM. That is exactly what will set value of hello as the result of the service.

Registering DXL Service

Currently DOORS Database Administrator has to add those services manually using DOORS Perms.

OSLCDXLService os = null
string err = null
string dxlCode = "#include <addins/services/helloWorld.inc>"
err = addOrUpdateOSLCDXLService("helloWorld", "Hello world in several languages", dxlCode, "getHelloString")
if (!null err){
      print err
} else {
      print "Installed Service\n"
}

Quick PERM reference (as it's not yet documented in DOORS DXL Reference Manual)

void setDxlServiceResult(string s)
sets s as the result for DXL Service

string addOrUpdateOSLCDXLService(string n, desc, dxlcode, entryPoint) 
registers dxlcode as the service with name n and description desc. entryPoint specifies the function in dxlcode to be run as entry point of service.

To make life easier OSLC DXL Services management will be soon available in File menu in IBM DOORS client.


Call OSLC DXL Service

Once you have your service registered you can send GET and PUT requests.
Set both the accept and content-type headers to this entry: application/rdf+xml and submit:
 
  • GET request for the DXL service description.
  • PUT request with optional parameters to invoke the service.

helloWorld service registered above could be accessed with URI like https://servername:portnumber/dwa/rm/dxl/helloWorld

As you notice before helloWorld function takes one parameter which should be supplied. You can provide parameters for DXL Services with aid of request content:


<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:doors="http://jazz.net/doors/xmlns/prod/jazz/doors/2.0/">
  <doors:Arguments>
  <doors:arguments>French</doors:arguments>
  </doors:Arguments>
</rdf:RDF>

If you have more then one argument, separate those with comas ','. All arguments are passed to DXL service as strings. If you need to have other types use DXL conversions in your service.


The results


Once DXL Service will finish its operation DWA server will return response with a content in following form:

  dwaOauth.js
was used to GET only protected information from DWA. But calling OSLC DXL Services requires PUT request which should be added.

There's nothing easier:

exports.getDXLServiceResult = function(req, res, dxlService, lang, next, onError) {
    var oa = new OAuth(req.session.oa._requestUrl,
              req.session.oa._accessUrl,
              req.session.oa._consumerKey,
              req.session.oa._consumerSecret,
              req.session.oa._version,
              req.session.oa._authorize_callback,
              req.session.oa._signatureMethod);

        // Try to get protected data
        var post_body = '<?xml version="1.0" encoding="UTF-8"?><rdf:RDF    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"    xmlns:doors="http://jazz.net/doors/xmlns/prod/jazz/doors/2.0/">'
        post_body += '<doors:Arguments><doors:arguments>' + lang +'</doors:arguments></doors:Arguments></rdf:RDF>';

        oa._headers['accept'] = "application/rdf+xml";

        var host = url.parse(location);
        console.log('requesting service ' +host.protocol+"://"+host.host+'/dwa/rm/dxl/'+dxlService + ' with param: '+lang);

        oa.put(host.protocol+"//"+host.host+'/dwa/rm/dxl/'+dxlService,
            req.session.oauth_access_token,
            req.session.oauth_access_token_secret,
            post_body, 
            'application/rdf+xml',
            function (error, data, response) {
                if (!error && next) {
                    next(req, res, data);
                }
                else if (onError) {
                    onError(error);
                }
            }
        );
}

And that's it!.

Friday, 22 May 2015

Node.js OAuth handshake with DWA

In first post I show you basic OAuth handshake between your Node.JS application and DOORS Web Access Server.

This post will be longer than other ones, here I will show you how to create application step-by-step. In next posts I will be just extending functionality of this one by adding some services or new methods.

What you'll need to build your application

If you do not have Git on your machine I can recommend Eclipse eGit, which nicely integrates Git software control manager (Git SCM) into your development environment.


Since this application is written in Node.JS you might also want to install Nodeclipse to get default formatting and language highlighting.

Any time you can also use command line tools for which you will need Node.JS and some Git client.

That's all you need to start your local server development, but we know things can quickly escalate so you might want to have a look at IBM Cloud services.

To the Clouds and beyond

If you need more services, processes, memory, storage, you might want to host your application on the Cloud. IBM Bluemix is a Next-Generation App development platform. All you need is your IBM ID and you can start developing cloud applications.

If you already have an IBM ID follow this link to begin your free trial. Otherwise you can create one using the IBM Bluemix registration page.

Once you have your IBM Bluemix account trial started a good idea would be to complete the Getting Started with IBM Bluemix tutorial.

Install dependencies

Before we can write the application we should provide Node.js dependencies. This demo application makes use of the following modules: express, oauth, request, cookie-parser and libxmljs.


These need added those to your package.json
{
  "name": "HelloDWAOAuth",
  "version": "0.0.1",
  "description": "A sample nodejs app for OAuth with DWA",
  "dependencies": {
    "express": "3.4.7",
    "jade": "^1.1.4",
                "libxmljs": "0.13.0",
                "cookie-parser":"",
                "oauth": "",
                "request":"",
                "util": "",
                "url" : ""
  },
  "engines": {
    "node": "0.10.*"
  },
  "repository": {}
}

Open command line and navigate to your project directory, and type:

> npm install

This will install all of the required dependencies based on your package.json

Note:

Sometimes you can get errors like:
The builds tools for Visual Studio 2010 (Platform Toolset = 'v100') cannot be found.install new module.
 

It might be useful to try –msvs_version=[2005, 2008, 2012] switch if default build will fail.

Writing the server application

This demo uses Express. You will need to create express instance, define routes and provide functions that might be required to execute those routes. Here is the basic code for an express server.

var express = require('express');

// setup middleware
var app = express();
app.use(app.router);
app.use(express.errorHandler());
app.use(express.static(__dirname + '/public')); //setup static public directory
app.set('view engine', 'jade');
app.set('views', __dirname + '/views'); //optional since express defaults to CWD/views

// render index page - main route
app.get('/', function(req, res){
                    res.render('index');
});

var appInfo = JSON.parse(process.env.VCAP_APPLICATION || "{}");
var services = JSON.parse(process.env.VCAP_SERVICES || "{}");
var host = (process.env.VCAP_APP_HOST || 'localhost');

// The port on the DEA for communication with the application:
var port = (process.env.VCAP_APP_PORT || 3000);
// Start server
app.listen(port, host);
console.log('App started on port ' + port);

16 lines of code is all you need for a basic HTTP server written in Node.js. Put it into a new app.js file and that will be an entry point for our application.

Certificates

If you will be working in a development environment you might need to use self-signed certificates.

Those by default will be rejected and no connection will be made. If you want to prevent self-signed certificate error add following line:
 process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

Adding OAuth to your application

Let's add OAuth as a separate Node.js module so it won't clatter the main app.js code. Modular approach will also help you to separate OAuth module and reuse it in other applications.

So now we will have app.js and Oauth.js files.

App.js will contain main routes and application logic for our sample. OAuth 1.0 requires user to provide application key and secret used to authenticate the application with a remote server. Thus our application will have two pages, where the user will be asked to provide location of rootservices, application key and OAuth secret.

Once those are provided the user will be redirected to page providing basic access to protected resources.

In this example we are using oauth Node.js module which comes with very useful examples how to do a basic Oauth handshake.

Configuring OAuth

Let's start with gathering information required to start OAuth. Create body.jade to add a form with POST action to /set_key_and_secret:

form(name="loginform", action="/set_key_and_secret", method="post")
          | Rootservices location:
          input(type="text", name="rootservices")
          br
          | Consumer Key:
          input(type="text", name="key")
          br
          | OAuth Secret:
          input(type="password", name="secret")
          br
          input(type="submit", value="Submit")


Now we have to add POST route to our application (app.js):
app.post('/set_key_and_secret', function(req, res) {
    //if (typeof req.body.rootservices !== 'undefined') console.log(req.body.rootservices);
    if (typeof req.body !== 'undefined') {
        if (typeof req.body.rootservices !== 'undefined' && req.body.rootservices != '' &&
            typeof req.body.key !== 'undefined' && req.body.key != '' &&
            typeof req.body.secret !== 'undefined' && req.body.sercret != '')
        {
            dwaOauth.configure(req.body.rootservices, req.body.key, req.body.secret, process.env.VCAP_APP_PORT || 3000);
            console.log("got settings");
            res.redirect("/dwa_login");
        }
        else {
            res.redirect('/');
        }
    }
});
Note: request.body is not available by default in Node.js express, we need to inform our application we would like to have access to this object. In order to do this we need to add:

app.use(express.bodyParser());

This must be added before any application routes will be defined. Best place is to add it before:

app.use(app.router);

dwaOauth.js module

You could wonder what is dwaOauth.configure called in preview example:

var location=null, oauth_key=null, oauth_secret=null, port=null;
exports.configure = function(rootservices_, key_, secret_, port_) {
    location = rootservices_;
    oauth_key = key_;
    oauth_secret = secret_;
    port = port_;
}

It is a very simple function used to provide some rootservices location, application key and secret to your dwaOauth module.

dwaOauth.js module will handle OAuth 1.0 dance and will give application access to protected resources.
All we need it to get authorized OAuth access token. The following steps are required to successfully get the authorized OAuth token:
  1. Login to DWA to get JSESSIONID Cookie
  2. Request a OAuth token
  3. Authorize the OAuth token
  4. Get the access token

In '/dwa_login' route we create a OAuth object which will be used to (2) request the OAuth token and once it was correctly retrieved it will redirect user to (3) authorize it. OAuth module will handle those requests and it will also redirect user to perform (1) login to DWA in order to get JSESSIONID Cookie.

In app.js add:

//Request an OAuth Request Token, and redirects the user to authorize it
app.get('/dwa_login', dwaOauth.getOAuthURLs, dwaOauth.requestToken);


In routes/dwaOauth.js add:

exports.requestToken = function(req, res) {
    var oa = new OAuth(oauthRequestTokenUrl,
        oauthAccessTokenUrl,
        oauth_key,
        oauth_secret,
        "1.0",
        req.protocol+"://"+req.host+":"+port+ "/dwa_cb", // this should work on Bluemix and localhost
        "HMAC-SHA1");

    console.log("1. Request a OAuth Token - this will redirect to DWA login page");

    oa.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results){
        if(error) {
            console.log('error : ' + util.inspect(error));
            res.end();
        }
        else {
            //store the tokens in the session
            req.session.oa = oa;
            req.session.oauth_token = oauth_token;
            req.session.oauth_token_secret = oauth_token_secret;

            console.log("2. Authorize the OAuth Token ");

            res.redirect(oauthUserAuthorizationUrl +"?oauth_token="+oauth_token);
        }
    });
}

Rootservices and OAuth

As you probably notice '/dwa_login' requires getOAuthURLs before it will be executed. getOAuthURLs method parses DWA rootservices so oauthRequestTokenUrl, oauthAccessTokenUrl and oauthUserAuthorizationUrl are discovered automatically. We do not need to store those in any configuration file.

// get URL from root services
exports.getOAuthURLs = function(req, res, next){
    console.log('Attemp to get OAuth URLs ' + location);
    if (location == null || oauth_key == null || oauth_secret == null) {
        return next('not configured');
    }

    request(location, function(error, response, body){
        if (!error && response.statusCode == 200) {
            var xmlDoc = libxmljs.parseXml(body);
            oauthRequestTokenUrl = xmlDoc.get('jfs:oauthRequestTokenUrl', ns.jfs).attr('resource').value();
            oauthAccessTokenUrl = xmlDoc.get('jfs:oauthAccessTokenUrl', ns.jfs).attr('resource').value();
            oauthUserAuthorizationUrl = xmlDoc.get('jfs:oauthUserAuthorizationUrl', ns.jfs).attr('resource').value();
            next();
        }
        else {
            console.log('error' + util.inspect(error));
            return next(error);
        }
    });
}
ns is defined as:
exports.ns = ns = {
  dcterms:{ 'dcterms': "http://purl.org/dc/terms/" },
  jfs: { 'jfs': 'http://jazz.net/xmlns/prod/jazz/jfs/1.0/' },
  oslc_rm:{ 'oslc_rm': 'http://open-services.net/ns/rm#' },
  oslc:   { 'oslc': 'http://open-services.net/ns/core#' },
  rdf: { 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' },
  doors:  { 'doors': 'http://jazz.net/doors/xmlns/prod/jazz/doors/2.0/' }
So namespaces are defined and easier to use.
Above code uses request to get rootservices and libxmljs to parse it. As mentioned earlier rootservices are the only resource not protected by OAuth so we do not need to worry about setting JSESSIONID cookie or OAuth while accessing it.

Add callback route

The last step in OAuth dance is to get the access token. We will do it in callback we provided earlier while constructing OAuth object. This callback is another route defined in app.js as '/dwa_cb':

// Callback for the authorisation page
app.get('/dwa_cb', dwaOauth.callback);

and in dwaOauth.js:

exports.callback = function(req, res) {
    var oa = new OAuth(req.session.oa._requestUrl,
            req.session.oa._accessUrl,
            req.session.oa._consumerKey,
            req.session.oa._consumerSecret,
            req.session.oa._version,
            req.session.oa._authorize_callback,
            req.session.oa._signatureMethod);
    console.log("3. Get the access token");

    oa.getOAuthAccessToken(
            req.session.oauth_token,
            req.session.oauth_token_secret,
            req.param('oauth_verifier'),
            function(error, oa_access_token, oa_access_token_secret, results) {
            if(error) {
                console.log('error' + error);
            }
            else {
                req.session.oauth_access_token = oa_access_token;
                req.session.oauth_access_token_secret = oa_access_token_secret;
                //redirect to home page
                res.redirect("/");
            }
    });
}

Access protected resource (finally!)

Once we have the access token we can access protected resources. Last line in '/dwa_cb' route will automatically redirect user to a root page which as we saw earlier routes to /resource.

Let's add a resources route to app.js:

app.get('/resource', checkLogin, function(req, res) {
    res.render('resource', {"resource": ""});
});


As you can see it renders 'resource' page with resource parameter. We will use HTTP Get request to get that resource. Add following to dwaOauth.js

exports.getProtectedResource = function(req, res, resourceURL, next, onError) {
    console.log('requesting protected resource: '+resourceURL);
    var oa = new OAuth(req.session.oa._requestUrl,
          req.session.oa._accessUrl,
          req.session.oa._consumerKey,
          req.session.oa._consumerSecret,
          req.session.oa._version,
          req.session.oa._authorize_callback,
          req.session.oa._signatureMethod);

    oa._headers['OSLC-Core-Version'] = '2.0';
    oa._headers['accept'] = "application/rdf+xml";
 
    // Try to get protected data
    oa.getProtectedResource(
        resourceURL,
        "GET",
        req.session.oauth_access_token,
        req.session.oauth_access_token_secret,
        function (error, data, response) {
            if (!error && next) {
                next(req, res, data);
            }
            else if (onError) {
                onError(error);
            }
        }
    );
}
This is a general HTTP request and should work with any OAuth server (DWA, DNG, QM, etc).

Once you have this your dwaOauth.js you can use it in your app.js with following route:

function getResource(req_, res_, next) {
    if (req_.body.res_url != "") {
        dwaOauth.getProtectedResource(req_, res_, req_.body.res_url, function(req, res, data) {
            var xmlDoc = libxmljs.parseXml(data);
            var mod = xmlDoc.get('//dcterms:title', dwaOauth.ns.dcterms).text();
            next(req_, res_, "", mod);
        });    
    }
    else next(req_, res_, "", "");
}

app.post('/resource', checkLogin, function(req, res) {
    getResource(req, res, render);

});


If you provide a valid resource link, below code will attempt to read dcterms:tile of the remote resource.
Update resource.jade to include a form for Resource URL input.

form(action="/resource", method="post")
      h2 Sample 1: Get resource title
      p Please provide a URL for one of your resources (say link to module)
      p Resource URL:
       input(type="text", name="res_url")
       input(type="submit", value="Submit")
       br
       if resource == ""
        p
       else
        p Resource title: '#{resource}'
        br

You just learned know how to read data from OAuth protected server! Easy ;)

You can find listing for dwaOauth.js here.

Update:

In my original post I forgot to mention checkLogin function. Thanks to Dani for pointing that!

// main redirection method
// make sure all your routes to protected resources will go via this method 
function checkLogin(req, res, next) {
    // if session doesn't have a oauth access token redirect to login page.
    if(!req.session.oauth_access_token) {
        res.redirect("/");
        return;
    }
    next();
}

but that's not all what's needed. It's only a redirection to the default route which I need to include here as well.

//root page
app.get('/', function(req, res){    

    // check if session has oAuth token
    if(!req.session.oauth_access_token) {
            res.render("index");
        }
        else {
            res.redirect("/resource");
        }
});
index is a simple form where user provides DWA location, oAuth key and secret:
//- index.jade
body
  table
    tr
      td(style= "width:30%;" )
        img(src="/images/newapp-icon.png")
      td
        h1 Hi there!
 
        p First you will need to setup your OAuth server details:
        
        form(name="loginform", action="/set_key_and_secret", method="post")
          | Rootservices location:
          input(type="text", name="rootservices")
          br
          | Consumer Key:
          input(type="text", name="key")
          br
          | OAuth Secret:
          input(type="password", name="secret")
          br
          input(type="submit", value="Submit")

set_key_and_secret is defined earlier.