-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add bpipe authentication with an application name #288
Changes from 5 commits
7d7ea83
a49b277
ef15196
a0ac289
55298bb
4d81364
d1d5e4a
4c6289f
97b7942
8b38f80
6350cc2
d86f4fe
b77f89c
1f29421
42c656e
f932bd1
9fd54f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,8 @@ | |
##' @param host A character option with either a machine name that is | ||
##' resolvable by DNS, or an IP address. Defaults to | ||
##' \sQuote{localhost}. | ||
##' @param app_name the name of an application that is authorized | ||
##' to connect to bpipe | ||
##' @return In the \code{default=TRUE} case nothing is returned, and | ||
##' this connection is automatically used for all future calls which | ||
##' omit the \code{con} argument. Otherwise a connection object is | ||
|
@@ -45,18 +47,23 @@ | |
##' environment. This effectively frees users from having to | ||
##' explicitly create such an object. | ||
##' @author Whit Armstrong and Dirk Eddelbuettel | ||
##' @seealso Many SAPI and bPipe connections require authentication | ||
##' @seealso Many SAPI and bPipe connections require authentication | ||
##' via \code{blpAuthenticate} after \code{blpConnect}. | ||
##' @examples | ||
##' \dontrun{ | ||
##' con <- blpConnect() # adjust as needed | ||
##' } | ||
blpConnect <- function(host=getOption("blpHost", "localhost"), | ||
port=getOption("blpPort", 8194L), | ||
default=TRUE) { | ||
default=TRUE, | ||
app_name = getOption("blpAppName", NULL)) { | ||
if (storage.mode(port) != "integer") port <- as.integer(port) | ||
if (storage.mode(host) != "character") stop("Host argument must be character.", call.=FALSE) | ||
con <- blpConnect_Impl(host, port) | ||
if(is.null(app_name)) { | ||
con <- blpConnect_Impl(host, port, NULL) | ||
} else { | ||
con <- blpConnect_Impl(host, port, app_name) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here the Or it might be better to change it to two lines including one comment that null/non-null is handled at the C(++) level? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a good point. I can make the change on Monday. I like the simplification with a note in the documentation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good! The code is in great shape but I want to hear from @johnlaing too. |
||
|
||
if (default) .pkgenv$con <- con else return(con) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
// authenticate.cpp -- Function to authenticate to Bloomberg backend | ||
// | ||
// Copyright (C) 2013 Whit Armstrong | ||
// Copyright (C) 2015 Whit Armstrong and Dirk Eddelbuettel | ||
// Copyright (C) 2015 Whit Armstrong and Dirk Eddelbuettelp | ||
// | ||
// This file is part of Rblpapi | ||
// | ||
|
@@ -22,6 +22,12 @@ | |
|
||
|
||
#include <string> | ||
#include <blpapi_defs.h> | ||
#include <blpapi_element.h> | ||
#include <blpapi_eventdispatcher.h> | ||
#include <blpapi_exception.h> | ||
#include <blpapi_name.h> | ||
#include <blpapi_request.h> | ||
#include <blpapi_session.h> | ||
#include <blpapi_event.h> | ||
#include <blpapi_message.h> | ||
|
@@ -36,6 +42,8 @@ using BloombergLP::blpapi::Request; | |
using BloombergLP::blpapi::Event; | ||
using BloombergLP::blpapi::Message; | ||
using BloombergLP::blpapi::MessageIterator; | ||
using BloombergLP::blpapi::CorrelationId; | ||
using BloombergLP::blpapi::EventQueue; | ||
|
||
static void identityFinalizer(SEXP identity_) { | ||
Identity* identity = reinterpret_cast<Identity*>(R_ExternalPtrAddr(identity_)); | ||
|
@@ -45,11 +53,7 @@ static void identityFinalizer(SEXP identity_) { | |
} | ||
} | ||
|
||
// Simpler interface | ||
// | ||
// [[Rcpp::export]] | ||
SEXP authenticate_Impl(SEXP con_, SEXP uuid_, SEXP ip_address_) { | ||
|
||
Identity* authenticateWithId(SEXP con_, SEXP uuid_, SEXP ip_address_) { | ||
// via Rcpp Attributes we get a try/catch block with error propagation to R "for free" | ||
Session* session = | ||
reinterpret_cast<Session*>(checkExternalPointer(con_, "blpapi::Session*")); | ||
|
@@ -93,5 +97,93 @@ SEXP authenticate_Impl(SEXP con_, SEXP uuid_, SEXP ip_address_) { | |
} | ||
if (event.eventType() == Event::RESPONSE) { break; } | ||
} | ||
return createExternalPointer<Identity>(identity_p,identityFinalizer,"blpapi::Identity*"); | ||
return identity_p; | ||
} | ||
|
||
Identity* authenticateWithApp(SEXP con_) { | ||
Identity* identity_p = 0; | ||
|
||
// via Rcpp Attributes we get a try/catch block with error propagation to R "for free" | ||
Session* session = | ||
reinterpret_cast<Session*>(checkExternalPointer(con_, "blpapi::Session*")); | ||
|
||
// setup authorization service | ||
std::string service("//blp/apiauth"); | ||
|
||
// authorize | ||
if (session->openService(service.c_str())) { | ||
Service authService = session->getService(service.c_str()); | ||
CorrelationId correlation_id(10); | ||
std::string token; | ||
EventQueue tokenEventQueue; | ||
session->generateToken(correlation_id, &tokenEventQueue); | ||
Event event = tokenEventQueue.nextEvent(); | ||
// | ||
// get token for session | ||
// | ||
if(event.eventType() == Event::TOKEN_STATUS || | ||
event.eventType() == Event::REQUEST_STATUS) { | ||
MessageIterator msgIter(event); | ||
while(msgIter.next()) { | ||
Message msg = msgIter.message(); | ||
if(msg.messageType() == "TokenGenerationSuccess") { | ||
token = msg.getElementAsString("token"); | ||
} | ||
else if(msg.messageType() == "TokenGenerationFailure") { | ||
Rcpp::stop("Failed to generate token"); | ||
} | ||
} | ||
} | ||
|
||
// | ||
// begin authorization | ||
// | ||
if(!token.empty()) { | ||
Request authRequest = authService.createAuthorizationRequest(); | ||
authRequest.set("token", token.c_str()); | ||
identity_p = new Identity(session->createIdentity()); | ||
session->sendAuthorizationRequest(authRequest, identity_p); | ||
// parse messages | ||
bool message_found = false; | ||
while(!message_found) { | ||
Event event = session->nextEvent(100000); | ||
if(event.eventType() == Event::RESPONSE || | ||
event.eventType() == Event::REQUEST_STATUS || | ||
event.eventType() == Event::PARTIAL_RESPONSE) { | ||
MessageIterator msgIter(event); | ||
while(msgIter.next()) { | ||
Message msg = msgIter.message(); | ||
if(msg.messageType() == "AuthorizationSuccess") { | ||
message_found = true; | ||
} | ||
else { | ||
Rcpp::stop(">>> Failed to Authorize"); | ||
} | ||
} | ||
} | ||
else if(event.eventType() == Event::TIMEOUT) { | ||
Rcpp::stop("Timed out trying to authorize"); | ||
} | ||
} | ||
} | ||
else { | ||
Rcpp::stop("Generated token was empty"); | ||
} | ||
} | ||
return identity_p; | ||
} | ||
|
||
// Simpler interface | ||
// | ||
// [[Rcpp::export]] | ||
SEXP authenticate_Impl(SEXP con_, SEXP uuid_, SEXP ip_address_) { | ||
Identity* identity_p = NULL; | ||
if(uuid_ == R_NilValue) { | ||
identity_p = authenticateWithApp(con_); | ||
} | ||
else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we put our else on the same line but hey... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More than happy to follow your preferred format. Since I am going to make the change above I will make the change here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See the 'Outdated' tag :) While I was at it I pushed some trivial changes back to your branch. So make sure you pull on Monday. |
||
identity_p = authenticateWithId(con_, uuid_, ip_address_); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the simpler switch here and the hand-off to these two specialised functions with the old default and new add-on. Nicely done. |
||
if(identity_p == NULL) { Rcpp::stop("Identity pointer is null\n"); } | ||
return createExternalPointer<Identity>(identity_p, identityFinalizer, "blpapi::Identity*"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stylistic nitpick: we should use something other than snake_case for argument names. We've tended toward camelCase in most functions, thought there are also a few old-school R dot.separated names left over. I think
appName
fits best here.