11454: (WIP) Login to remote clusters using federated identity.
authorLucas Di Pentima <ldipentima@veritasgenetics.com>
Mon, 22 Jan 2018 18:16:20 +0000 (15:16 -0300)
committerLucas Di Pentima <ldipentima@veritasgenetics.com>
Mon, 22 Jan 2018 18:16:20 +0000 (15:16 -0300)
Expanded the multi site search's SessionDB to allow logins
using salted tokens.
Pending: Do the right thing on every combination of federation
settings.

Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima@veritasgenetics.com>

apps/workbench/app/assets/javascripts/application.js
apps/workbench/app/assets/javascripts/components/search.js
apps/workbench/app/assets/javascripts/models/session_db.js
apps/workbench/app/views/search/index.html [deleted file]
apps/workbench/config/application.default.yml
apps/workbench/npm_packages

index b90081f46fe9d5ccdec360165e6bc2528817d7b2..270a4c766d3152f3edd487561cc40ae4e2bdb256 100644 (file)
@@ -34,6 +34,7 @@
 //= require npm-dependencies
 //= require mithril/stream/stream
 //= require awesomplete
+//= require jssha
 //= require_tree .
 
 Es6ObjectAssign.polyfill()
index 2fe73193e7f116fcda6c2831c8c2250c2a266aee..5cb292bd7ab7709a3004278a7e1a20810e389c0e 100644 (file)
@@ -90,7 +90,7 @@ window.SearchResultsTable = {
 
 window.Search = {
     oninit: function(vnode) {
-        vnode.state.sessionDB = new SessionDB()
+        vnode.state.sessionDB = new SessionDB(vnode.attrs.remoteHosts)
         vnode.state.searchEntered = m.stream()
         vnode.state.searchActive = m.stream()
         // When searchActive changes (e.g., when restoring state
@@ -154,7 +154,8 @@ window.Search = {
         })
     },
     view: function(vnode) {
-        var sessions = vnode.state.sessionDB.loadAll()
+        // FIXME: The line below seems superfluous
+        // var sessions = vnode.state.sessionDB.loadAll()
         return m('form', {
             onsubmit: function() {
                 vnode.state.searchActive(vnode.state.searchEntered())
index a43cd79545b66ff47dda07f3097b3c8803a47262..28c5cc40788083459bc605e64ac244d99c69a3bb 100644 (file)
@@ -2,9 +2,10 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-window.SessionDB = function() {
+window.SessionDB = function(rhosts) {
     var db = this
     Object.assign(db, {
+        remoteHosts: rhosts || [],
         discoveryCache: {},
         loadFromLocalStorage: function() {
             try {
@@ -81,7 +82,10 @@ window.SessionDB = function() {
             // also call checkForNewToken() on (at least) its first
             // render. Otherwise, the login procedure can't be
             // completed.
-            document.location = baseURL + 'login?return_to=' + encodeURIComponent(document.location.href.replace(/\?.*/, '')+'?baseURL='+encodeURIComponent(baseURL))
+            var remoteAPI = new URL(baseURL)
+            db.saltedToken(remoteAPI.hostname.split('.')[0]).then(function(token) {
+                document.location = baseURL + 'login?return_to=' + encodeURIComponent(document.location.href.replace(/\?.*/, '')+'?baseURL='+encodeURIComponent(baseURL)) + '&api_token='+encodeURIComponent(token)
+            })
             return false
         },
         logout: function(k) {
@@ -92,6 +96,28 @@ window.SessionDB = function() {
             delete sessions[k].token
             db.save(k, sessions[k])
         },
+        saltedToken: function(uuid_prefix) {
+            // Takes a cluster UUID prefix and returns a salted token to allow
+            // log into said cluster using federated identity.
+            var session = db.loadLocal()
+            var st = m.stream()
+            return db.request(session, '/arvados/v1/api_client_authorizations', {
+                data: {
+                    filters: JSON.stringify([['api_token', '=', session.token]]),
+                }
+            }).then(function(resp) {
+                if (resp.items.length == 1) {
+                    var token_uuid = resp.items[0].uuid
+                    if (token_uuid.length !== '') {
+                        var shaObj = new jsSHA("SHA-1", "TEXT")
+                        shaObj.setHMACKey(session.token, "TEXT")
+                        shaObj.update(uuid_prefix)
+                        var hmac = shaObj.getHMAC("HEX")
+                        return 'v2/' + token_uuid + '/' + hmac
+                    } else { return null }
+                }
+            }).catch(function(err) { return null })
+        },
         checkForNewToken: function() {
             // If there's a token and baseURL in the location bar (i.e.,
             // we just landed here after a successful login), save it and
@@ -127,7 +153,7 @@ window.SessionDB = function() {
                     },
                 }).then(function(user) {
                     session.user = user
-                    db.save(user.uuid.slice(0, 5), session)
+                    db.save(user.owner_uuid.slice(0, 5), session)
                     db.trash(key)
                 })
             })
diff --git a/apps/workbench/app/views/search/index.html b/apps/workbench/app/views/search/index.html
deleted file mode 100644 (file)
index 6bcad0b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- Copyright (C) The Arvados Authors. All rights reserved.
-
-SPDX-License-Identifier: AGPL-3.0 -->
-
-<div data-mount-mithril="Search"></div>
index 187845038ea3c48449ccd1e7d1c002657ffe6e37..4cf7b5ed2675a322d83ae0241efbf7b4969391d8 100644 (file)
@@ -313,4 +313,9 @@ common:
   #
   # Link to use for Arvados Workflow Composer app, or false if not available.
   #
-  composer_url: false
\ No newline at end of file
+  composer_url: false
+
+  # Multi site search federation feature:
+  # * remote_hosts: List of API servers that allow logins from accounts on this
+  #   workbench's cluster.
+  remote_hosts: []
index c126b559fb138387b27424773cd931053f6bcc86..64f58ac4686f4686eccc18854bfc9da6a91d7432 100644 (file)
@@ -8,6 +8,7 @@
 npm 'browserify', require: false
 npm 'jquery'
 npm 'awesomplete'
+npm 'jssha'
 
 npm 'mithril'
 npm 'es6-object-assign'