Make sure that the bearer token is only sent when interacting with the server of the ConnectedUser. Remove the token of places it is not needed.

This commit is contained in:
Feufochmar 2020-04-05 17:36:16 +02:00
parent 0cb6a8555a
commit f38597c738
9 changed files with 63 additions and 49 deletions

View File

@ -133,7 +133,7 @@ const UI = {
'my-inbox': function() {
UI.updateNav('inbox-selector')
if (UI.is_connected) {
UI.showTimeline(ConnectedUser.actor.data.inbox, ConnectedUser.tokens.user.access_token)
UI.showTimeline(ConnectedUser.actor.data.inbox)
UI.showPage('show-profile', ConnectedUser.actor)
} else {
UI.showTimeline(undefined, undefined)
@ -143,7 +143,7 @@ const UI = {
'my-outbox': function() {
UI.updateNav('outbox-selector')
if (UI.is_connected) {
UI.showTimeline(ConnectedUser.actor.data.outbox, ConnectedUser.tokens.user.access_token)
UI.showTimeline(ConnectedUser.actor.data.outbox)
UI.showPage('show-profile', ConnectedUser.actor)
} else {
UI.showTimeline(undefined, undefined)
@ -312,7 +312,7 @@ const UI = {
UI.refresh_page[page](data)
},
// Show the timeline
showTimeline: function(url, token) {
showTimeline: function(url) {
if (url) {
Elem('timeline').style.display = 'block'
Elem('timeline-data').innerHTML = 'Loading collection...'
@ -321,7 +321,6 @@ const UI = {
UI.timeline = new Timeline()
UI.timeline.load(
url,
token,
function(load_ok, failure_message) {
if (load_ok) {
Elem('timeline-data').innerHTML = UI.timeline.activities.map(function(activity) {
@ -473,12 +472,12 @@ const UI = {
// Timeline navigation
nextTimeline: function() {
if (UI.timeline.next) {
UI.showTimeline(UI.timeline.next, UI.timeline.token)
UI.showTimeline(UI.timeline.next)
}
},
prevTimeline: function() {
if (UI.timeline.prev) {
UI.showTimeline(UI.timeline.prev, UI.timeline.token)
UI.showTimeline(UI.timeline.prev)
}
},
// Show contents of activities

View File

@ -2,9 +2,8 @@ const {Actor} = require('./actor.js')
const {KnownActors} = require('./known-actors.js')
// ActivityObject class: wrapper around an Object of the ActivityStream spec
const ActivityObject = function(as_object, token) {
const ActivityObject = function(as_object) {
this.data = as_object
this.token = token
// Those should be available
this.id = this.data.id
this.type = this.data.type
@ -31,7 +30,6 @@ ActivityObject.prototype = {
// Fetch attributes
this.data.fetchAttributeList(
['attributedTo', 'attachment'],
this.token,
function (load_ok, failure_message) {
if (load_ok) {
// Actor
@ -85,7 +83,7 @@ ActivityObject.prototype = {
var err = error_msg
if (typeof act === 'string') {
// string => id of actor
KnownActors.retrieve(act, undefined, function (load_ok, actor, failure_message) {
KnownActors.retrieve(act, function (load_ok, actor, failure_message) {
if (load_ok) {
to.push(actor)
} else {

View File

@ -74,21 +74,22 @@ const ASMention = function() {}
// Object fetcher
// Convert resources from the resource fetcher into Activity objects
const {TokenCollection} = require('./token-collection.js')
Fetcher = {
// Cache of already accessed objects
knownObjects: {},
// Get an object
// Callback signature is function(load_ok, fetched_object, failure_message)
get: function(id, token, callback) {
get: function(id, callback) {
if (Fetcher.knownObjects[id]) {
callback(true, Fetcher.knownObjects[id], '')
} else {
Fetcher.refresh(id, token, callback)
Fetcher.refresh(id, callback)
}
},
// Refresh a resource
// Callback signature is function(load_ok, fetched_object, failure_message)
refresh: function(id, token, callback) {
refresh: function(id, callback) {
const request = new XMLHttpRequest()
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
@ -107,6 +108,7 @@ Fetcher = {
}
}
request.open('GET', id, true)
const token = TokenCollection.get(id)
if (token) {
request.setRequestHeader('Authorization', 'Bearer ' + token)
}
@ -251,7 +253,7 @@ ASBase.prototype = {
// This method fetch and convert an attribute in a usable format
// Callback signature is function(load_ok, failure_message)
// Note: done this way to avoid fetching all resources in fromJson, but only when they are needed
fetchAttribute: function(attribute_name, token, callback) {
fetchAttribute: function(attribute_name, callback) {
const attribute_value = this[attribute_name]
if (this._alwaysAvailable.includes(attribute_name)) {
// Those attributes are always available in a usable format, no need to fetch
@ -264,7 +266,7 @@ ASBase.prototype = {
callback(true, undefined)
} else if (Array.isArray(attribute_value)) {
// Attribute is an array of elements. Each element should be converted.
this._fetchAndConvertAllAttributeValue(attribute_value.values(), token, function(load_ok, fetched_value, failure_message) {
this._fetchAndConvertAllAttributeValue(attribute_value.values(), function(load_ok, fetched_value, failure_message) {
// Update value of attribute
this[attribute_name] = fetched_value
callback(load_ok, failure_message)
@ -274,7 +276,7 @@ ASBase.prototype = {
callback(true, undefined)
} else {
// Convertion needed
this._fetchAndConvertAttributeValue(attribute_value, token, function(load_ok, fetched_value, failure_message) {
this._fetchAndConvertAttributeValue(attribute_value, function(load_ok, fetched_value, failure_message) {
// Update value of attribute
this[attribute_name] = fetched_value
callback(load_ok, failure_message)
@ -283,12 +285,12 @@ ASBase.prototype = {
},
// Fetch all elements of an array iterator to populate a result array
// Callback signature is function(load_ok, fetched_value, failure_message)
_fetchAndConvertAllAttributeValue: function(iter, token, callback, ret_value, previous_errors) {
_fetchAndConvertAllAttributeValue: function(iter, callback, ret_value, previous_errors) {
const next = iter.next()
if (next.done) {
callback(true, ret_value, (previous_errors === '') ? undefined : previous_errors)
} else {
this._fetchAndConvertAttributeValue(next.value, token, function(load_ok, fetched_value, failure_message) {
this._fetchAndConvertAttributeValue(next.value, function(load_ok, fetched_value, failure_message) {
var values = ret_value ? ret_value : []
const msg_errors = (previous_errors ? previous_errors : '') + (load_ok ? '' : '<br/>' + failure_message)
if (load_ok) {
@ -298,13 +300,13 @@ ASBase.prototype = {
console.log(failure_message)
}
// Next
this._fetchAndConvertAllAttributeValue(iter, token, callback, values, msg_errors)
this._fetchAndConvertAllAttributeValue(iter, callback, values, msg_errors)
}.bind(this))
}
},
// Fetch an attribute value
// Callback signature is function(load_ok, fetched_value, failure_message)
_fetchAndConvertAttributeValue: function(attribute_value, token, callback) {
_fetchAndConvertAttributeValue: function(attribute_value, callback) {
if ((attribute_value === undefined) || (attribute_value === null)) {
// attribute is not present, return it as is
callback(true, attribute_value, undefined)
@ -314,7 +316,7 @@ ASBase.prototype = {
callback(true, Fetcher.fromJson(attribute_value), undefined)
} else if (typeof attribute_value === 'string') {
// Link => fetch value
Fetcher.get(attribute_value, token, function(load_ok, obj, failure_message) {
Fetcher.get(attribute_value, function(load_ok, obj, failure_message) {
if (load_ok) {
callback(true, obj, undefined)
} else {
@ -329,23 +331,23 @@ ASBase.prototype = {
},
// Fetch several attributes at the same time
// Callback signature is function(load_ok, failure_message)
fetchAttributeList: function (attribute_lst, token, callback) {
this._fetchAttributeListIter(attribute_lst.values(), token, callback, undefined)
fetchAttributeList: function (attribute_lst, callback) {
this._fetchAttributeListIter(attribute_lst.values(), callback, undefined)
},
//
_fetchAttributeListIter: function (attribute_lst, token, callback, previous_errors) {
_fetchAttributeListIter: function (attribute_lst, callback, previous_errors) {
const next = attribute_lst.next()
if (next.done) {
callback(true, (previous_errors === '') ? undefined : previous_errors)
} else {
this.fetchAttribute(next.value, token, function(load_ok, failure_message) {
this.fetchAttribute(next.value, function(load_ok, failure_message) {
const msg_errors = (previous_errors ? previous_errors : '') + (load_ok ? '' : '<br/>' + failure_message)
if (!load_ok) {
// Don't stop at first error, but cumulate error messages
console.log(failure_message)
}
// Next
this._fetchAttributeListIter(attribute_lst, token, callback, msg_errors)
this._fetchAttributeListIter(attribute_lst, callback, msg_errors)
}.bind(this))
}
}

View File

@ -3,9 +3,8 @@ const {KnownActors} = require('./known-actors.js')
const {ActivityObject} = require('./activity-object.js')
// Activity class
const Activity = function (as_activity, token) {
const Activity = function (as_activity) {
this.data = as_activity
this.token = token
// Those should be available
this.id = this.data.id
this.type = this.data.type
@ -26,12 +25,11 @@ Activity.prototype = {
// actor, object
this.data.fetchAttributeList(
['actor', 'object'],
this.token,
function (load_ok, failure_message) {
if (load_ok) {
// Object
if (this.data.object) {
this.object = new ActivityObject(this.data.object, this.token)
this.object = new ActivityObject(this.data.object)
} else {
this.object = undefined
}
@ -103,7 +101,7 @@ Activity.prototype = {
var err = error_msg
if (typeof act === 'string') {
// string => id of actor
KnownActors.retrieve(act, undefined, function (load_ok, actor, failure_message) {
KnownActors.retrieve(act, function (load_ok, actor, failure_message) {
if (load_ok) {
to.push(actor)
} else {

View File

@ -82,7 +82,7 @@ Actor.prototype = {
// - a string indicating the failure
loadFromProfileUrl: function(profile_url, callback) {
// Fetch the activity actor
Fetcher.get(profile_url, undefined, function(load_ok, fetched_actor, failure_message) {
Fetcher.get(profile_url, function(load_ok, fetched_actor, failure_message) {
if (load_ok) {
this.loadFromASActor(fetched_actor, callback)
} else {
@ -97,7 +97,6 @@ Actor.prototype = {
// Fetch a few properties
this.data.fetchAttributeList(
['preferredUsername', 'name', 'summary', 'icon', 'endpoints'],
undefined,
function (ok, error) {
if (ok) {
if (!this.name) {

View File

@ -1,3 +1,4 @@
const {TokenCollection} = require('./token-collection.js')
const {Actor} = require('./actor.js')
// Connected user structure
@ -27,6 +28,8 @@ const ConnectedUser = {
ConnectedUser.tokens.user.access_token = window.localStorage.getItem('access_token:' + user_name + '@' + server_name)
// Load the rest with webfinger / activity pub requests
ConnectedUser.actor.loadFromNameServerAddress(user_name + '@' + server_name, callback)
// Set the token to use when connecting to the server
TokenCollection.set(server_name, ConnectedUser.tokens.user.access_token)
} else {
callback(false, 'user not connected')
}
@ -44,6 +47,8 @@ const ConnectedUser = {
if (ConnectedUser.actor.name && ConnectedUser.actor.server) {
window.localStorage.removeItem('refresh_token:' + ConnectedUser.actor.name + '@' + ConnectedUser.actor.server)
window.localStorage.removeItem('access_token:' + ConnectedUser.actor.name + '@' + ConnectedUser.actor.server)
// Unset the token to use when connecting to the server
TokenCollection.set(ConnectedUser.actor.server, undefined)
}
window.localStorage.removeItem('last:server.name')
window.localStorage.removeItem('last:user.name')
@ -123,6 +128,8 @@ const ConnectedUser = {
ConnectedUser.tokens.user.access_token = answer.access_token
// Save the tokens
ConnectedUser.saveToLocalStorage()
//
TokenCollection.set(ConnectedUser.actor.server, ConnectedUser.tokens.user.access_token)
// OK, return to callback
callback(true, undefined)
} else if (request.readyState == 4) {

View File

@ -19,7 +19,7 @@ const KnownActors = {
},
// Retrieve an actor
// Callback takes three arguments: load_ok, retrieved_actor, failure_message
retrieve: function(profile, token, callback) {
retrieve: function(profile, callback) {
if (KnownActors.get(profile)) {
callback(true, KnownActors.get(profile), undefined)
} else {

View File

@ -25,23 +25,20 @@ Timeline.prototype = {
prev: undefined,
// Link to next page
next: undefined,
// Token used when loading -- for loading prev/next within the same context
token: undefined,
// Load a timeline from a url
load: function(url, token, callback) {
this.token = token
load: function(url, callback) {
// A timeline must be reloaded each time
Fetcher.refresh(url, token, function(load_ok, fetched_timeline, failure_message) {
Fetcher.refresh(url, function(load_ok, fetched_timeline, failure_message) {
if (load_ok) {
if (fetched_timeline.type === 'OrderedCollection' && fetched_timeline.first) {
// Collection is paginated
// Fetch the attribute
fetched_timeline.fetchAttribute('first', token, function (ok, error) {
fetched_timeline.fetchAttribute('first', function (ok, error) {
if (load_ok) {
this.activities = []
this.prev = fetched_timeline.prev
this.next = fetched_timeline.next
this.parsePage(fetched_timeline.first, token, callback)
this.parsePage(fetched_timeline.first, callback)
} else {
callback(false, error)
}
@ -51,12 +48,12 @@ Timeline.prototype = {
this.activities = []
this.prev = fetched_timeline.prev
this.next = fetched_timeline.next
this.parsePage(fetched_timeline, token, callback)
this.parsePage(fetched_timeline, callback)
} else if (fetched_timeline.type === 'OrderedCollectionPage') {
this.activities = []
this.prev = fetched_timeline.prev
this.next = fetched_timeline.next
this.parsePage(fetched_timeline, token, callback)
this.parsePage(fetched_timeline, callback)
} else {
callback(false, 'Unexpected answer from server when fetching activity collection.')
console.log(answer)
@ -67,25 +64,25 @@ Timeline.prototype = {
}
}.bind(this))
},
parsePage: function (collectionPage, token, callback) {
parsePage: function (collectionPage, callback) {
// Fetch attributes of collectionPage
collectionPage.fetchAttribute('orderedItems', token, function (load_ok, failure_message) {
collectionPage.fetchAttribute('orderedItems', function (load_ok, failure_message) {
if (load_ok) {
// Elements of the page have been fetched
// For each, convert them to Activity and put them in this.activities
this.addActivity(collectionPage.orderedItems.values(), token, callback, '')
this.addActivity(collectionPage.orderedItems.values(), callback, '')
} else {
callback(false, failure_message)
}
}.bind(this))
},
addActivity: function (iter, token, callback, error_msg) {
addActivity: function (iter, callback, error_msg) {
const next = iter.next()
if (next.done) {
callback(true, error_msg === '' ? undefined : error_msg)
} else {
var err = error_msg
const act = new Activity(next.value, token)
const act = new Activity(next.value)
act.loadNeeded(function (load_ok, failure_message) {
if (!load_ok) {
err = err + failure_message + '<br/>'
@ -95,7 +92,7 @@ Timeline.prototype = {
// Store in known activities
KnownActivities.set(act.id, act)
// next
this.addActivity(iter, token, callback, err)
this.addActivity(iter, callback, err)
}.bind(this))
}
}

14
src/token-collection.js Normal file
View File

@ -0,0 +1,14 @@
// A small object for getting the token when talking to a server
TokenCollection = {
tokens: {},
set: function (server, token) {
TokenCollection.tokens[server] = token
},
get: function (url) {
const server = url.replace('http://', '').replace('https://', '').split(/[/?#]/)[0]
return TokenCollection.tokens[server]
}
}
// Exported structures
exports.TokenCollection = TokenCollection