Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions Bio-KBase-Auth/lib/Bio/KBase/AuthToken.pm
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,19 @@ sub get {
my $headers;
if ( $self->{'user_id'} && $self->{'password'}) {
$headers = HTTP::Headers->new;
# In case there is 'auth_service_url' defined in constructor we're going to
# make authentication call to '/Sessions/Login' function of KBase auth_service
# (communicating with GlobusOnline on service side) rather than communicate
# with GlobusOnline directly from here.
if ( $self->{'auth_service_url'}) {
# Here we use '/Sessions/Login' function of KBase auth_service sending
# user_id and password there and getting valid token back.
my $user_map = $self->auth_service_fetch_user(
"auth_service_url" => $self->{'auth_service_url'},
"path" => '/Sessions/Login', "body" => 'user_id=' . $self->{'user_id'} .
'&password=' . $self->{'password'} . '&fields=token');
return($self->token( $user_map->{'token'}));
}
$headers->authorization_basic( $self->{'user_id'}, $self->{'password'});
$headers{'Authorization'} = $headers->header('Authorization');
} else {
Expand Down Expand Up @@ -486,6 +499,19 @@ sub validate {
if ( $cached && $cached eq $vars{'un'} ) {
$verify = 1;
} else {
# In case there is 'auth_service_url' defined in constructor we're going to
# make authentication call to '/Sessions/Login' function of KBase auth_service
# (communicating with GlobusOnline on service side) rather than communicate
# with GlobusOnline directly from here.
if ( $self->{'auth_service_url'}) {
# Here we use '/Sessions/Login' function of KBase auth_service sending
# token there for validation and getting user_id back.
my $user_map = $self->auth_service_fetch_user(
"auth_service_url" => $self->{'auth_service_url'},
"path" => '/Sessions/Login', "body" => 'token=' . $self->{'token'} .
'&fields=user_id');
$verify = exists($user_map->{'user_id'})
} else {
# Check cache for signer public key
my($response, $binary_sig, $client);
$binary_sig = pack('H*',$vars{'sig'});
Expand Down Expand Up @@ -513,6 +539,7 @@ sub validate {
$rsa->use_sha1_hash();

$verify = $rsa->verify($sig_data,$binary_sig);
}
if ($verify) {
# write the sha1 hash of the token into the cache
# we don't actually want to store the tokens themselves
Expand Down Expand Up @@ -585,6 +612,56 @@ sub go_request {

}

# function that handles KBase auth_service requests
# takes the following params in hash
# auth_service_url => auth_service end-point
# path => relative service path part of the URL,
# doesn't include service end-point
# method => (GET|PUT|POST|DELETE) defaults to POST
# body => string for http content; Content-Type will be
# set to application/x-www-form-urlencoded
# automatically
#
# Returns a hashref to the json data that was returned
# throw an exception if there is an error, make sure you
# trap this with an eval{}!

sub auth_service_fetch_user {
my $self = shift @_;
my %p = @_;
my $json;
eval {
my $baseurl = $p{'auth_service_url'};
my %headers;
unless ($p{'path'}) {
die "No path specified";
}
$headers{'Content-Type'} = 'application/x-www-form-urlencoded';
my $headers = HTTP::Headers->new( %headers);
my $client = LWP::UserAgent->new(default_headers => $headers);
$client->timeout(5);
$client->ssl_opts(verify_hostname => 0);
my $method = $p{'method'} ? $p{'method'} : "POST";
my $url = sprintf('%s%s', $baseurl,$p{'path'});
my $req = HTTP::Request->new($method, $url);
if ($p{'body'}) {
$req->content( $p{'body'});
}
my $response = $client->request( $req);
unless ($response->is_success) {
die $response->status_line;
}
$json = decode_json( $response->content());
#$json = $self->_SquashJSONBool( $json);
};
if ($@) {
die "Failed to query Globus Online: $@";
} else {
return( $json);
}
}


sub _SquashJSONBool {
# Walk an object ref returned by from_json() and squash references
# to JSON::XS::Boolean into a simple 0 or 1
Expand Down
10 changes: 8 additions & 2 deletions python-libs/biokbase/nexus/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,25 @@ def __init__(self, config=None, config_file=None):
self.cache = cache_impl_class(*cache_config.get('args', []))
self.cache = token_utils.LoggingCacheWrapper(self.cache)

def validate_token(self, token):
def validate_token(self, token, auth_service_url=None):
"""
Validate that a token was issued for the specified user and client by
the server in the SigningSubject.

:param token: An authentication token provided by the client.

:param auth_service_url: An optional url of KBase auth_service; In case
auth_service_url is defined we're going to make authentication request
to '/Sessions/Login' function of KBase auth_service (communicating with
GlobusOnline on service side) rather than communicate with GlobusOnline
directly.

:return: username, client id and the server that issued the token.

:raises ValueError: If the signature is invalid, the token is expired or
the public key could not be gotten.
"""
return token_utils.validate_token(token, self.cache, self.verify_ssl)
return token_utils.validate_token(token, self.cache, self.verify_ssl, auth_service_url)


def generate_request_url(self, username=None):
Expand Down
30 changes: 28 additions & 2 deletions python-libs/biokbase/nexus/token_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,19 @@ def get_public_key(self, key_id):
log.debug(message.format(self.cache.__class__.__name__, key_id))
return self.cache.get_public_key(key_id)

def validate_token(token, cache=InMemoryCache(), verify=True):
def validate_token(token, cache=InMemoryCache(), verify=True, auth_service_url=None):
"""
Given a request or access token validate it.

Keyword arguments:
:param tokens: A signed authentication token which was provided by Nexus

:param auth_service_url: An optional url of KBase auth_service; In case
auth_service_url is not None we're going to make authentication request
to '/Sessions/Login' function of KBase auth_service (communicating with
GlobusOnline on service side) rather than communicate with GlobusOnline
directly.

:raises ValueError: If the signature is invalid, the token is expired or
the public key could not be gotten.
"""
Expand All @@ -121,7 +127,27 @@ def validate_token(token, cache=InMemoryCache(), verify=True):
for entry in unencoded_token.split('|'):
key, value = entry.split('=')
token_map[key] = value

if auth_service_url:
# Since auth_service_url was passed authentication request to
# '/Sessions/Login' function of KBase auth_service will be used
# rather than direct communication with GlobusOnline.
now = time.mktime(datetime.utcnow().timetuple())
if token_map['expiry'] < now:
raise ValueError('TokenExpired')
headers = {'Content-type': 'application/x-www-form-urlencoded'}
url = auth_service_url + '/Sessions/Login'
# token will be sent to auth_service for validation and user_id
# will be returned back.
data = 'token=' + unencoded_token + '&fields=user_id'
ret = requests.post(url, headers = headers, data = data)
status = ret.status_code
if status >= 200 and status <= 299:
# Parse response in JSON format, it's a map containing requested
# user properties (only user_id in our case).
user_map = json.loads(ret.text)
else:
raise ValueError('Failed to verify token: ' + ret.text)
return (token_map['un'], user_map['user_id'], '')
# If the public key is not already in the cache, cache it keyed by the signing subject.
if not cache.has_public_key(token_map['SigningSubject']):
response = requests.get(token_map['SigningSubject'], verify=verify)
Expand Down