update rcs token refresh to use new endpoint
added a higher order function for generating a refresh function with and initial jwt. this was moved from the serviceRCELoader to its own module since it could be used by other modules in the future. i also made some other improvements like returning a promise to make error handleing possible (while keeping the existing callback functionality), and not making extra api requests if the function is called multiple times before the the api responds. closes CNVS-35199 test plan: - edit a wiki page with rcs enabled - wait an hour - or - temporariy change the expiration of jwts in canvas - open lib/canvas/security/services_jwt.rb - change the 3600 in the create_payload class method to 30 - reload the edit page - wait 30 seconds instead - make sure you can still make api requests - click load more on something, upload a file, etc Change-Id: I21c89bfc82d4bf1ae3ff25b467b5f6d1e674cfac Reviewed-on: https://gerrit.instructure.com/103303 Reviewed-by: Simon Williams <simon@instructure.com> Tested-by: Jenkins QA-Review: Tucker McKnight <tmcknight@instructure.com> Product-Review: Brent Burgoyne <bburgoyne@instructure.com>
This commit is contained in:
parent
b36f17b65b
commit
d225415e9f
|
@ -0,0 +1,24 @@
|
|||
import axios from 'axios'
|
||||
|
||||
export function refreshFn (initialToken) {
|
||||
let token = initialToken
|
||||
let promise = null
|
||||
|
||||
return (done) => {
|
||||
if (promise === null) {
|
||||
promise = axios
|
||||
.post('/api/v1/jwts/refresh', { jwt: token })
|
||||
.then((resp) => {
|
||||
promise = null
|
||||
token = resp.data.token
|
||||
return token
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof done === 'function') {
|
||||
promise.then(done)
|
||||
}
|
||||
|
||||
return promise
|
||||
}
|
||||
}
|
|
@ -1,16 +1,11 @@
|
|||
import $ from 'jquery'
|
||||
import _ from 'underscore'
|
||||
import {refreshFn as refreshToken} from 'jsx/shared/jwt'
|
||||
import editorOptions from 'jsx/shared/rce/editorOptions'
|
||||
import loadEventListeners from 'jsx/shared/rce/loadEventListeners'
|
||||
import polyfill from 'jsx/shared/rce/polyfill'
|
||||
import splitAssetString from 'compiled/str/splitAssetString'
|
||||
|
||||
let refreshToken = function(callback){
|
||||
return $.post("/api/v1/jwts").done((response)=>{
|
||||
callback(response.token)
|
||||
})
|
||||
}
|
||||
|
||||
let RCELoader = {
|
||||
preload() {
|
||||
this.loadRCE(function(){})
|
||||
|
@ -32,7 +27,7 @@ import splitAssetString from 'compiled/str/splitAssetString'
|
|||
let context = splitAssetString(ENV.context_asset_string)
|
||||
let props = {
|
||||
jwt: ENV.JWT,
|
||||
refreshToken: refreshToken,
|
||||
refreshToken: refreshToken(ENV.JWT),
|
||||
host: ENV.RICH_CONTENT_APP_HOST,
|
||||
canUploadFiles: ENV.RICH_CONTENT_CAN_UPLOAD_FILES,
|
||||
contextType: context[0],
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
define [
|
||||
'jquery'
|
||||
'jsx/shared/rce/serviceRCELoader'
|
||||
'jsx/shared/jwt',
|
||||
'helpers/editorUtils'
|
||||
'helpers/fakeENV'
|
||||
'helpers/fixtures'
|
||||
], ($, RCELoader, editorUtils, fakeENV, fixtures) ->
|
||||
], ($, RCELoader, jwt, editorUtils, fakeENV, fixtures) ->
|
||||
QUnit.module 'loadRCE',
|
||||
setup: ->
|
||||
fakeENV.setup()
|
||||
|
@ -160,6 +161,8 @@ define [
|
|||
@sidebar = {}
|
||||
@rce = { renderSidebarIntoDiv: sinon.stub().callsArgWith(2, @sidebar) }
|
||||
sinon.stub(RCELoader, 'loadRCE').callsArgWith(0, @rce)
|
||||
@refreshToken = sinon.spy()
|
||||
@stub(jwt, 'refreshFn').returns(@refreshToken)
|
||||
|
||||
teardown: ->
|
||||
fakeENV.teardown()
|
||||
|
@ -191,7 +194,8 @@ define [
|
|||
RCELoader.loadSidebarOnTarget(@$div, cb)
|
||||
ok @rce.renderSidebarIntoDiv.called
|
||||
props = @rce.renderSidebarIntoDiv.args[0][1]
|
||||
equal(typeof props.refreshToken, 'function')
|
||||
ok jwt.refreshFn.calledWith(props.jwt)
|
||||
equal(props.refreshToken, @refreshToken)
|
||||
|
||||
test 'passes brand config json url', ->
|
||||
ENV.active_brand_config_json_url = {}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
define([
|
||||
'jsx/shared/jwt',
|
||||
'axios'
|
||||
], (jwt, axios) => {
|
||||
let thenStub
|
||||
let promise
|
||||
let token
|
||||
let newToken
|
||||
let refreshFn
|
||||
|
||||
QUnit.module('Jwt refreshFn', {
|
||||
setup () {
|
||||
token = 'testjwt'
|
||||
newToken = 'newtoken'
|
||||
promise = Promise.resolve(newToken)
|
||||
sinon.spy(promise, 'then')
|
||||
thenStub = sinon.stub().returns(promise)
|
||||
this.stub(axios, 'post').returns({ then: thenStub })
|
||||
refreshFn = jwt.refreshFn(token)
|
||||
}
|
||||
})
|
||||
|
||||
test('posts token to refresh endpoint', () => {
|
||||
refreshFn()
|
||||
ok(axios.post.calledWithMatch('/api/v1/jwts/refresh', { jwt: token }))
|
||||
})
|
||||
|
||||
test('only posts once if called multiple times before response', () => {
|
||||
refreshFn()
|
||||
refreshFn()
|
||||
equal(axios.post.callCount, 1)
|
||||
})
|
||||
|
||||
test('returns promise resolved after request', () => {
|
||||
equal(refreshFn(), promise)
|
||||
})
|
||||
|
||||
test('calls callbacks with new token', () => {
|
||||
const spy = sinon.spy()
|
||||
refreshFn(spy)
|
||||
ok(promise.then.calledWith(spy))
|
||||
})
|
||||
|
||||
test('gets token from response data', () => {
|
||||
refreshFn()
|
||||
equal(thenStub.firstCall.args[0]({ data: { token: newToken } }), newToken)
|
||||
})
|
||||
|
||||
test('updates token in closure', () => {
|
||||
refreshFn()
|
||||
thenStub.firstCall.args[0]({ data: { token: newToken } })
|
||||
refreshFn()
|
||||
ok(axios.post.calledWithMatch('/api/v1/jwts/refresh', { jwt: newToken }))
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue