Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.
Merged
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
25 changes: 16 additions & 9 deletions src/CallHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ import SdkConfig from './SdkConfig';
import { showUnknownDeviceDialogForCalls } from './cryptodevices';
import WidgetUtils from './utils/WidgetUtils';
import WidgetEchoStore from './stores/WidgetEchoStore';
import ScalarAuthClient from './ScalarAuthClient';
import {IntegrationManagers} from "./integrations/IntegrationManagers";

global.mxCalls = {
//room_id: MatrixCall
Expand Down Expand Up @@ -348,14 +348,20 @@ async function _startCallApp(roomId, type) {
// the state event in anyway, but the resulting widget would then not
// work for us. Better that the user knows before everyone else in the
// room sees it.
const scalarClient = new ScalarAuthClient();
let haveScalar = false;
try {
await scalarClient.connect();
haveScalar = scalarClient.hasCredentials();
} catch (e) {
// fall through
const managers = IntegrationManagers.sharedInstance();
let haveScalar = true;
if (managers.hasManager()) {
try {
const scalarClient = managers.getPrimaryManager().getScalarClient();
await scalarClient.connect();
haveScalar = scalarClient.hasCredentials();
} catch (e) {
// ignore
}
} else {
haveScalar = false;
}

if (!haveScalar) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");

Expand Down Expand Up @@ -421,7 +427,8 @@ async function _startCallApp(roomId, type) {
// URL, but this will at least allow the integration manager to not be hardcoded.
widgetUrl = SdkConfig.get().integrations_jitsi_widget_url + '?' + queryString;
} else {
widgetUrl = SdkConfig.get().integrations_rest_url + '/widgets/jitsi.html?' + queryString;
const apiUrl = IntegrationManagers.sharedInstance().getPrimaryManager().apiUrl;
widgetUrl = apiUrl + '/widgets/jitsi.html?' + queryString;
}

const widgetData = { widgetSessionId };
Expand Down
13 changes: 7 additions & 6 deletions src/FromWidgetPostMessageApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import WidgetMessagingEndpoint from './WidgetMessagingEndpoint';
import ActiveWidgetStore from './stores/ActiveWidgetStore';
import MatrixClientPeg from "./MatrixClientPeg";
import RoomViewStore from "./stores/RoomViewStore";
import { showIntegrationsManager } from './integrations/integrations';
import {IntegrationManagers} from "./integrations/IntegrationManagers";

const WIDGET_API_VERSION = '0.0.2'; // Current API version
const SUPPORTED_WIDGET_API_VERSIONS = [
Expand Down Expand Up @@ -193,11 +193,12 @@ export default class FromWidgetPostMessageApi {
const integType = (data && data.integType) ? data.integType : null;
const integId = (data && data.integId) ? data.integId : null;

showIntegrationsManager({
room: MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()),
screen: 'type_' + integType,
integrationId: integId,
});
// TODO: Open the right integration manager for the widget
IntegrationManagers.sharedInstance().getPrimaryManager().open(
MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()),
`type_${integType}`,
integId,
);
} else if (action === 'set_always_on_screen') {
// This is a new message: there is no reason to support the deprecated widgetData here
const data = event.data.data;
Expand Down
69 changes: 45 additions & 24 deletions src/ScalarAuthClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,43 @@ import * as Matrix from 'matrix-js-sdk';
// The version of the integration manager API we're intending to work with
const imApiVersion = "1.1";

class ScalarAuthClient {
constructor() {
export default class ScalarAuthClient {
constructor(apiUrl, uiUrl) {
this.apiUrl = apiUrl;
this.uiUrl = uiUrl;
this.scalarToken = null;
// `undefined` to allow `startTermsFlow` to fallback to a default
// callback if this is unset.
this.termsInteractionCallback = undefined;

// We try and store the token on a per-manager basis, but need a fallback
// for the default manager.
const configApiUrl = SdkConfig.get()['integrations_rest_url'];
const configUiUrl = SdkConfig.get()['integrations_ui_url'];
this.isDefaultManager = apiUrl === configApiUrl && configUiUrl === uiUrl;
}

/**
* Determines if setting up a ScalarAuthClient is even possible
* @returns {boolean} true if possible, false otherwise.
*/
static isPossible() {
return SdkConfig.get()['integrations_rest_url'] && SdkConfig.get()['integrations_ui_url'];
_writeTokenToStore() {
window.localStorage.setItem("mx_scalar_token_at_" + this.apiUrl, this.scalarToken);
if (this.isDefaultManager) {
// We remove the old token from storage to migrate upwards. This is safe
// to do because even if the user switches to /app when this is on /develop
// they'll at worst register for a new token.
window.localStorage.removeItem("mx_scalar_token"); // no-op when not present
}
}

_readTokenFromStore() {
let token = window.localStorage.getItem("mx_scalar_token_at_" + this.apiUrl);
if (!token && this.isDefaultManager) {
token = window.localStorage.getItem("mx_scalar_token");
}
return token;
}

_readToken() {
if (this.scalarToken) return this.scalarToken;
return this._readTokenFromStore();
}

setTermsInteractionCallback(callback) {
Expand All @@ -61,8 +84,7 @@ class ScalarAuthClient {

// Returns a promise that resolves to a scalar_token string
getScalarToken() {
let token = this.scalarToken;
if (!token) token = window.localStorage.getItem("mx_scalar_token");
const token = this._readToken();

if (!token) {
return this.registerForToken();
Expand All @@ -78,7 +100,7 @@ class ScalarAuthClient {
}

_getAccountName(token) {
const url = SdkConfig.get().integrations_rest_url + "/account";
const url = this.apiUrl + "/account";

return new Promise(function(resolve, reject) {
request({
Expand Down Expand Up @@ -111,7 +133,7 @@ class ScalarAuthClient {
return token;
}).catch((e) => {
if (e instanceof TermsNotSignedError) {
console.log("Integrations manager requires new terms to be agreed to");
console.log("Integration manager requires new terms to be agreed to");
// The terms endpoints are new and so live on standard _matrix prefixes,
// but IM rest urls are currently configured with paths, so remove the
// path from the base URL before passing it to the js-sdk
Expand All @@ -126,7 +148,7 @@ class ScalarAuthClient {
// Once we've fully transitioned to _matrix URLs, we can give people
// a grace period to update their configs, then use the rest url as
// a regular base url.
const parsedImRestUrl = url.parse(SdkConfig.get().integrations_rest_url);
const parsedImRestUrl = url.parse(this.apiUrl);
parsedImRestUrl.path = '';
parsedImRestUrl.pathname = '';
return startTermsFlow([new Service(
Expand All @@ -147,17 +169,18 @@ class ScalarAuthClient {
return MatrixClientPeg.get().getOpenIdToken().then((tokenObject) => {
// Now we can send that to scalar and exchange it for a scalar token
return this.exchangeForScalarToken(tokenObject);
}).then((tokenObject) => {
}).then((token) => {
// Validate it (this mostly checks to see if the IM needs us to agree to some terms)
return this._checkToken(tokenObject);
}).then((tokenObject) => {
window.localStorage.setItem("mx_scalar_token", tokenObject);
return tokenObject;
return this._checkToken(token);
}).then((token) => {
this.scalarToken = token;
this._writeTokenToStore();
return token;
});
}

exchangeForScalarToken(openidTokenObject) {
const scalarRestUrl = SdkConfig.get().integrations_rest_url;
const scalarRestUrl = this.apiUrl;

return new Promise(function(resolve, reject) {
request({
Expand All @@ -181,7 +204,7 @@ class ScalarAuthClient {
}

getScalarPageTitle(url) {
let scalarPageLookupUrl = SdkConfig.get().integrations_rest_url + '/widgets/title_lookup';
let scalarPageLookupUrl = this.apiUrl + '/widgets/title_lookup';
scalarPageLookupUrl = this.getStarterLink(scalarPageLookupUrl);
scalarPageLookupUrl += '&curl=' + encodeURIComponent(url);

Expand Down Expand Up @@ -217,7 +240,7 @@ class ScalarAuthClient {
* @return {Promise} Resolves on completion
*/
disableWidgetAssets(widgetType, widgetId) {
let url = SdkConfig.get().integrations_rest_url + '/widgets/set_assets_state';
let url = this.apiUrl + '/widgets/set_assets_state';
url = this.getStarterLink(url);
return new Promise((resolve, reject) => {
request({
Expand Down Expand Up @@ -246,7 +269,7 @@ class ScalarAuthClient {
getScalarInterfaceUrlForRoom(room, screen, id) {
const roomId = room.roomId;
const roomName = room.name;
let url = SdkConfig.get().integrations_ui_url;
let url = this.uiUrl;
url += "?scalar_token=" + encodeURIComponent(this.scalarToken);
url += "&room_id=" + encodeURIComponent(roomId);
url += "&room_name=" + encodeURIComponent(roomName);
Expand All @@ -264,5 +287,3 @@ class ScalarAuthClient {
return starterLinkUrl + "?scalar_token=" + encodeURIComponent(this.scalarToken);
}
}

module.exports = ScalarAuthClient;
5 changes: 3 additions & 2 deletions src/ScalarMessaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,13 @@ Example:
}
*/

import SdkConfig from './SdkConfig';
import MatrixClientPeg from './MatrixClientPeg';
import { MatrixEvent } from 'matrix-js-sdk';
import dis from './dispatcher';
import WidgetUtils from './utils/WidgetUtils';
import RoomViewStore from './stores/RoomViewStore';
import { _t } from './languageHandler';
import {IntegrationManagers} from "./integrations/IntegrationManagers";

function sendResponse(event, res) {
const data = JSON.parse(JSON.stringify(event.data));
Expand Down Expand Up @@ -548,7 +548,8 @@ const onMessage = function(event) {
// (See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
let configUrl;
try {
configUrl = new URL(SdkConfig.get().integrations_ui_url);
// TODO: Support multiple integration managers
configUrl = new URL(IntegrationManagers.sharedInstance().getPrimaryManager().uiUrl);
} catch (e) {
// No integrations UI URL, ignore silently.
return;
Expand Down
31 changes: 22 additions & 9 deletions src/components/views/elements/AppTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import qs from 'querystring';
import React from 'react';
import PropTypes from 'prop-types';
import MatrixClientPeg from '../../../MatrixClientPeg';
import ScalarAuthClient from '../../../ScalarAuthClient';
import WidgetMessaging from '../../../WidgetMessaging';
import AccessibleButton from './AccessibleButton';
import Modal from '../../../Modal';
Expand All @@ -35,7 +34,7 @@ import WidgetUtils from '../../../utils/WidgetUtils';
import dis from '../../../dispatcher';
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
import classNames from 'classnames';
import { showIntegrationsManager } from '../../../integrations/integrations';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";

const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
const ENABLE_REACT_PERF = false;
Expand Down Expand Up @@ -178,9 +177,22 @@ export default class AppTile extends React.Component {
return;
}

const managers = IntegrationManagers.sharedInstance();
if (!managers.hasManager()) {
console.warn("No integration manager - not setting scalar token", url);
this.setState({
error: null,
widgetUrl: this._addWurlParams(this.props.url),
initialising: false,
});
return;
}

// TODO: Pick the right manager for the widget

// Fetch the token before loading the iframe as we need it to mangle the URL
if (!this._scalarClient) {
this._scalarClient = new ScalarAuthClient();
this._scalarClient = managers.getPrimaryManager().getScalarClient();
}
this._scalarClient.getScalarToken().done((token) => {
// Append scalar_token as a query param if not already present
Expand All @@ -189,7 +201,7 @@ export default class AppTile extends React.Component {
const params = qs.parse(u.query);
if (!params.scalar_token) {
params.scalar_token = encodeURIComponent(token);
// u.search must be set to undefined, so that u.format() uses query paramerters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
// u.search must be set to undefined, so that u.format() uses query parameters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
u.search = undefined;
u.query = params;
}
Expand Down Expand Up @@ -251,11 +263,12 @@ export default class AppTile extends React.Component {
if (this.props.onEditClick) {
this.props.onEditClick();
} else {
showIntegrationsManager({
room: this.props.room,
screen: 'type_' + this.props.type,
integrationId: this.props.id,
});
// TODO: Open the right manager for the widget
IntegrationManagers.sharedInstance().getPrimaryManager().open(
this.props.room,
this.props.type,
this.props.id,
);
}
}

Expand Down
12 changes: 8 additions & 4 deletions src/components/views/elements/ManageIntegsButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import ScalarAuthClient from '../../../ScalarAuthClient';
import { _t } from '../../../languageHandler';
import { showIntegrationsManager } from '../../../integrations/integrations';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";

export default class ManageIntegsButton extends React.Component {
constructor(props) {
Expand All @@ -30,12 +29,17 @@ export default class ManageIntegsButton extends React.Component {
onManageIntegrations = (ev) => {
ev.preventDefault();

showIntegrationsManager({ room: this.props.room });
const managers = IntegrationManagers.sharedInstance();
if (!managers.hasManager()) {
managers.openNoManagerDialog();
} else {
managers.getPrimaryManager().open(this.props.room);
}
};

render() {
let integrationsButton = <div />;
if (ScalarAuthClient.isPossible()) {
if (IntegrationManagers.sharedInstance().hasManager()) {
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
integrationsButton = (
<AccessibleButton
Expand Down
13 changes: 10 additions & 3 deletions src/components/views/messages/TextualBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import highlight from 'highlight.js';
import * as HtmlUtils from '../../../HtmlUtils';
import {formatDate} from '../../../DateUtils';
import sdk from '../../../index';
import ScalarAuthClient from '../../../ScalarAuthClient';
import Modal from '../../../Modal';
import SdkConfig from '../../../SdkConfig';
import dis from '../../../dispatcher';
Expand All @@ -35,6 +34,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import ReplyThread from "../elements/ReplyThread";
import {host as matrixtoHost} from '../../../matrix-to';
import {pillifyLinks} from '../../../utils/pillify';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";

module.exports = React.createClass({
displayName: 'TextualBody',
Expand Down Expand Up @@ -318,12 +318,19 @@ module.exports = React.createClass({
// which requires the user to click through and THEN we can open the link in a new tab because
// the window.open command occurs in the same stack frame as the onClick callback.

const managers = IntegrationManagers.sharedInstance();
if (!managers.hasManager()) {
managers.openNoManagerDialog();
return;
}

// Go fetch a scalar token
const scalarClient = new ScalarAuthClient();
const integrationManager = managers.getPrimaryManager();
const scalarClient = integrationManager.getScalarClient();
scalarClient.connect().then(() => {
const completeUrl = scalarClient.getStarterLink(starterLink);
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const integrationsUrl = SdkConfig.get().integrations_ui_url;
const integrationsUrl = integrationManager.uiUrl;
Modal.createTrackedDialog('Add an integration', '', QuestionDialog, {
title: _t("Add an Integration"),
description:
Expand Down
Loading