diff --git a/packages/@uppy/companion-client/src/Provider.js b/packages/@uppy/companion-client/src/Provider.js index 081f3cdc65..41790b13f3 100644 --- a/packages/@uppy/companion-client/src/Provider.js +++ b/packages/@uppy/companion-client/src/Provider.js @@ -7,6 +7,26 @@ const getName = (id) => { return id.split('-').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' ') } +function getOrigin () { + // eslint-disable-next-line no-restricted-globals + return location.origin +} + +function getRegex (value) { + if (typeof value === 'string') { + return new RegExp(`^${value}$`) + } if (value instanceof RegExp) { + return value + } + return undefined +} + +function isOriginAllowed (origin, allowedOrigin) { + const patterns = Array.isArray(allowedOrigin) ? allowedOrigin.map(getRegex) : [getRegex(allowedOrigin)] + return patterns + .some((pattern) => pattern?.test(origin) || pattern?.test(`${origin}/`)) // allowing for trailing '/' +} + export default class Provider extends RequestClient { #refreshingTokenPromise @@ -72,7 +92,10 @@ export default class Provider extends RequestClient { } authUrl (queries = {}) { - const params = new URLSearchParams(queries) + const params = new URLSearchParams({ + state: btoa(JSON.stringify({ origin: getOrigin() })), + ...queries, + }) if (this.preAuthToken) { params.set('uppyPreAuthToken', this.preAuthToken) } @@ -80,6 +103,50 @@ export default class Provider extends RequestClient { return `${this.hostname}/${this.id}/connect?${params}` } + async login (queries) { + await this.ensurePreAuth() + + return new Promise((resolve, reject) => { + const link = this.authUrl(queries) + const authWindow = window.open(link, '_blank') + const handleToken = (e) => { + if (e.source !== authWindow) { + this.uppy.log.warn('ignoring event from unknown source', e) + return + } + + const { companionAllowedHosts } = this.uppy.getPlugin(this.pluginId).opts + if (!isOriginAllowed(e.origin, companionAllowedHosts)) { + reject(new Error(`rejecting event from ${e.origin} vs allowed pattern ${companionAllowedHosts}`)) + return + } + + // Check if it's a string before doing the JSON.parse to maintain support + // for older Companion versions that used object references + const data = typeof e.data === 'string' ? JSON.parse(e.data) : e.data + + if (data.error) { + const { uppy } = this + const message = uppy.i18n('authAborted') + uppy.info({ message }, 'warning', 5000) + reject(new Error('auth aborted')) + return + } + + if (!data.token) { + reject(new Error('did not receive token from auth window')) + return + } + + authWindow.close() + window.removeEventListener('message', handleToken) + this.setAuthToken(data.token) + resolve() + } + window.addEventListener('message', handleToken) + }) + } + refreshTokenUrl () { return `${this.hostname}/${this.id}/refresh-token` } diff --git a/packages/@uppy/locales/src/en_US.js b/packages/@uppy/locales/src/en_US.js index 0970e6dede..b7a16db1cd 100644 --- a/packages/@uppy/locales/src/en_US.js +++ b/packages/@uppy/locales/src/en_US.js @@ -89,6 +89,7 @@ en_US.strings = { importFiles: 'Import files from:', importFrom: 'Import from %{name}', inferiorSize: 'This file is smaller than the allowed size of %{size}', + loadedXFiles: 'Loaded %{numFiles} files', loading: 'Loading...', logOut: 'Log out', micDisabled: 'Microphone access denied by user', diff --git a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx index c4ccfccbf6..5a96f893fd 100644 --- a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx @@ -13,25 +13,6 @@ import View from '../View.js' import packageJson from '../../package.json' -function getOrigin () { - // eslint-disable-next-line no-restricted-globals - return location.origin -} - -function getRegex (value) { - if (typeof value === 'string') { - return new RegExp(`^${value}$`) - } if (value instanceof RegExp) { - return value - } - return undefined -} -function isOriginAllowed (origin, allowedOrigin) { - const patterns = Array.isArray(allowedOrigin) ? allowedOrigin.map(getRegex) : [getRegex(allowedOrigin)] - return patterns - .some((pattern) => pattern?.test(origin) || pattern?.test(`${origin}/`)) // allowing for trailing '/' -} - function formatBreadcrumbs (breadcrumbs) { return breadcrumbs.slice(1).map((directory) => directory.name).join('/') } @@ -254,45 +235,14 @@ export default class ProviderView extends View { } async handleAuth () { - await this.provider.ensurePreAuth() - - const authState = btoa(JSON.stringify({ origin: getOrigin() })) const clientVersion = `@uppy/provider-views=${ProviderView.VERSION}` - const link = this.provider.authUrl({ state: authState, uppyVersions: clientVersion }) - - const authWindow = window.open(link, '_blank') - const handleToken = (e) => { - if (e.source !== authWindow) { - this.plugin.uppy.log('rejecting event from unknown source') - return - } - if (!isOriginAllowed(e.origin, this.plugin.opts.companionAllowedHosts) || e.source !== authWindow) { - this.plugin.uppy.log(`rejecting event from ${e.origin} vs allowed pattern ${this.plugin.opts.companionAllowedHosts}`) - } - - // Check if it's a string before doing the JSON.parse to maintain support - // for older Companion versions that used object references - const data = typeof e.data === 'string' ? JSON.parse(e.data) : e.data - - if (data.error) { - this.plugin.uppy.log('auth aborted', 'warning') - const { uppy } = this.plugin - const message = uppy.i18n('authAborted') - uppy.info({ message }, 'warning', 5000) - return - } - - if (!data.token) { - this.plugin.uppy.log('did not receive token from auth window', 'error') - return - } - - authWindow.close() - window.removeEventListener('message', handleToken) - this.provider.setAuthToken(data.token) + try { + await this.provider.login({ uppyVersions: clientVersion }) + this.plugin.setPluginState({ authenticated: true }) this.preFirstRender() + } catch (e) { + this.plugin.uppy.log(`login failed: ${e.message}`) } - window.addEventListener('message', handleToken) } async handleScroll (event) { diff --git a/private/locale-pack/test.mjs b/private/locale-pack/test.mjs index e525d824f5..885e4fb56e 100644 --- a/private/locale-pack/test.mjs +++ b/private/locale-pack/test.mjs @@ -13,7 +13,7 @@ const root = fileURLToPath(new URL('../../', import.meta.url)) const leadingLocaleName = 'en_US' const mode = process.argv[2] const pluginLocaleDependencies = { - core: 'provider-views', + core: ['provider-views', 'companion-client'], } function getAllFilesPerPlugin (pluginNames) { @@ -30,9 +30,9 @@ function getAllFilesPerPlugin (pluginNames) { filesPerPlugin[name] = getFiles(name) if (name in pluginLocaleDependencies) { - filesPerPlugin[name].push( - getFiles(pluginLocaleDependencies[name]), - ) + for (const subDeb of pluginLocaleDependencies[name]) { + filesPerPlugin[name].push(...getFiles(subDeb)) + } } }