Add a prompt when interacting with an identity server without terms
This adds a prompt whenever we are about to perform some action on a default identity server (from homeserver .well-known or Riot app config) without terms. This allows the user to abort or trust the server (storing it in account data). Fixes https://github.com/vector-im/riot-web/issues/10557
This commit is contained in:
parent
54cea6a5e3
commit
0fc5108817
4 changed files with 81 additions and 19 deletions
|
@ -17,7 +17,18 @@ limitations under the License.
|
||||||
import { createClient, SERVICE_TYPES } from 'matrix-js-sdk';
|
import { createClient, SERVICE_TYPES } from 'matrix-js-sdk';
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import MatrixClientPeg from './MatrixClientPeg';
|
||||||
|
import Modal from './Modal';
|
||||||
|
import sdk from './index';
|
||||||
|
import { _t } from './languageHandler';
|
||||||
import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
|
import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
|
||||||
|
import {
|
||||||
|
doesAccountDataHaveIdentityServer,
|
||||||
|
doesIdentityServerHaveTerms,
|
||||||
|
useDefaultIdentityServer,
|
||||||
|
} from './utils/IdentityServerUtils';
|
||||||
|
import { abbreviateUrl } from './utils/UrlUtils';
|
||||||
|
|
||||||
|
export class AbortedIdentityActionError extends Error {}
|
||||||
|
|
||||||
export default class IdentityAuthClient {
|
export default class IdentityAuthClient {
|
||||||
/**
|
/**
|
||||||
|
@ -89,7 +100,10 @@ export default class IdentityAuthClient {
|
||||||
try {
|
try {
|
||||||
await this._checkToken(token);
|
await this._checkToken(token);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof TermsNotSignedError) {
|
if (
|
||||||
|
e instanceof TermsNotSignedError ||
|
||||||
|
e instanceof AbortedIdentityActionError
|
||||||
|
) {
|
||||||
// Retrying won't help this
|
// Retrying won't help this
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -106,6 +120,8 @@ export default class IdentityAuthClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _checkToken(token) {
|
async _checkToken(token) {
|
||||||
|
const identityServerUrl = this._matrixClient.getIdentityServerUrl();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._matrixClient.getIdentityAccount(token);
|
await this._matrixClient.getIdentityAccount(token);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -113,7 +129,7 @@ export default class IdentityAuthClient {
|
||||||
console.log("Identity Server requires new terms to be agreed to");
|
console.log("Identity Server requires new terms to be agreed to");
|
||||||
await startTermsFlow([new Service(
|
await startTermsFlow([new Service(
|
||||||
SERVICE_TYPES.IS,
|
SERVICE_TYPES.IS,
|
||||||
this._matrixClient.getIdentityServerUrl(),
|
identityServerUrl,
|
||||||
token,
|
token,
|
||||||
)]);
|
)]);
|
||||||
return;
|
return;
|
||||||
|
@ -121,6 +137,40 @@ export default class IdentityAuthClient {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.tempClient &&
|
||||||
|
!doesAccountDataHaveIdentityServer() &&
|
||||||
|
!await doesIdentityServerHaveTerms(identityServerUrl)
|
||||||
|
) {
|
||||||
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
|
const { finished } = Modal.createTrackedDialog('Default identity server terms warning', '',
|
||||||
|
QuestionDialog, {
|
||||||
|
title: _t("Identity server has no terms of service"),
|
||||||
|
description: <div>
|
||||||
|
<p>{_t(
|
||||||
|
"This action requires accessing the default identity server " +
|
||||||
|
"<server /> to validate an email address or phone number, but the server " +
|
||||||
|
"does not have any terms of service.", {},
|
||||||
|
{
|
||||||
|
server: () => <b>{abbreviateUrl(identityServerUrl)}</b>,
|
||||||
|
},
|
||||||
|
)}</p>
|
||||||
|
<p>{_t(
|
||||||
|
"Only continue if you trust the owner of the server.",
|
||||||
|
)}</p>
|
||||||
|
</div>,
|
||||||
|
button: _t("Trust"),
|
||||||
|
});
|
||||||
|
const [confirmed] = await finished;
|
||||||
|
if (confirmed) {
|
||||||
|
useDefaultIdentityServer();
|
||||||
|
} else {
|
||||||
|
throw new AbortedIdentityActionError(
|
||||||
|
"User aborted identity server action without terms",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We should ensure the token in `localStorage` is cleared
|
// We should ensure the token in `localStorage` is cleared
|
||||||
// appropriately. We already clear storage on sign out, but we'll need
|
// appropriately. We already clear storage on sign out, but we'll need
|
||||||
// additional clearing when changing ISes in settings as part of future
|
// additional clearing when changing ISes in settings as part of future
|
||||||
|
|
|
@ -24,9 +24,8 @@ import Modal from '../../../Modal';
|
||||||
import dis from "../../../dispatcher";
|
import dis from "../../../dispatcher";
|
||||||
import { getThreepidsWithBindStatus } from '../../../boundThreepids';
|
import { getThreepidsWithBindStatus } from '../../../boundThreepids';
|
||||||
import IdentityAuthClient from "../../../IdentityAuthClient";
|
import IdentityAuthClient from "../../../IdentityAuthClient";
|
||||||
import {SERVICE_TYPES} from "matrix-js-sdk";
|
|
||||||
import {abbreviateUrl, unabbreviateUrl} from "../../../utils/UrlUtils";
|
import {abbreviateUrl, unabbreviateUrl} from "../../../utils/UrlUtils";
|
||||||
import { getDefaultIdentityServerUrl } from '../../../utils/IdentityServerUtils';
|
import { getDefaultIdentityServerUrl, doesIdentityServerHaveTerms } from '../../../utils/IdentityServerUtils';
|
||||||
|
|
||||||
// We'll wait up to this long when checking for 3PID bindings on the IS.
|
// We'll wait up to this long when checking for 3PID bindings on the IS.
|
||||||
const REACHABILITY_TIMEOUT = 10000; // ms
|
const REACHABILITY_TIMEOUT = 10000; // ms
|
||||||
|
@ -162,19 +161,8 @@ export default class SetIdServer extends React.Component {
|
||||||
let save = true;
|
let save = true;
|
||||||
|
|
||||||
// Double check that the identity server even has terms of service.
|
// Double check that the identity server even has terms of service.
|
||||||
let terms;
|
const hasTerms = await doesIdentityServerHaveTerms(fullUrl);
|
||||||
try {
|
if (!hasTerms) {
|
||||||
terms = await MatrixClientPeg.get().getTerms(SERVICE_TYPES.IS, fullUrl);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
if (e.cors === "rejected" || e.httpStatus === 404) {
|
|
||||||
terms = null;
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!terms || !terms["policies"] || Object.keys(terms["policies"]).length <= 0) {
|
|
||||||
const [confirmed] = await this._showNoTermsWarning(fullUrl);
|
const [confirmed] = await this._showNoTermsWarning(fullUrl);
|
||||||
save = confirmed;
|
save = confirmed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,10 @@
|
||||||
"Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s",
|
"Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s",
|
||||||
"Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:",
|
"Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:",
|
||||||
"Unnamed Room": "Unnamed Room",
|
"Unnamed Room": "Unnamed Room",
|
||||||
|
"Identity server has no terms of service": "Identity server has no terms of service",
|
||||||
|
"This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.": "This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.",
|
||||||
|
"Only continue if you trust the owner of the server.": "Only continue if you trust the owner of the server.",
|
||||||
|
"Trust": "Trust",
|
||||||
"Error": "Error",
|
"Error": "Error",
|
||||||
"Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.",
|
"Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.",
|
||||||
"Dismiss": "Dismiss",
|
"Dismiss": "Dismiss",
|
||||||
|
@ -563,9 +567,7 @@
|
||||||
"Change identity server": "Change identity server",
|
"Change identity server": "Change identity server",
|
||||||
"Disconnect from the identity server <current /> and connect to <new /> instead?": "Disconnect from the identity server <current /> and connect to <new /> instead?",
|
"Disconnect from the identity server <current /> and connect to <new /> instead?": "Disconnect from the identity server <current /> and connect to <new /> instead?",
|
||||||
"Terms of service not accepted or the identity server is invalid.": "Terms of service not accepted or the identity server is invalid.",
|
"Terms of service not accepted or the identity server is invalid.": "Terms of service not accepted or the identity server is invalid.",
|
||||||
"Identity server has no terms of service": "Identity server has no terms of service",
|
|
||||||
"The identity server you have chosen does not have any terms of service.": "The identity server you have chosen does not have any terms of service.",
|
"The identity server you have chosen does not have any terms of service.": "The identity server you have chosen does not have any terms of service.",
|
||||||
"Only continue if you trust the owner of the server.": "Only continue if you trust the owner of the server.",
|
|
||||||
"Disconnect identity server": "Disconnect identity server",
|
"Disconnect identity server": "Disconnect identity server",
|
||||||
"Disconnect from the identity server <idserver />?": "Disconnect from the identity server <idserver />?",
|
"Disconnect from the identity server <idserver />?": "Disconnect from the identity server <idserver />?",
|
||||||
"Disconnect": "Disconnect",
|
"Disconnect": "Disconnect",
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { SERVICE_TYPES } from 'matrix-js-sdk';
|
||||||
import SdkConfig from '../SdkConfig';
|
import SdkConfig from '../SdkConfig';
|
||||||
import MatrixClientPeg from '../MatrixClientPeg';
|
import MatrixClientPeg from '../MatrixClientPeg';
|
||||||
|
|
||||||
|
@ -28,3 +29,24 @@ export function useDefaultIdentityServer() {
|
||||||
base_url: url,
|
base_url: url,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function doesIdentityServerHaveTerms(fullUrl) {
|
||||||
|
let terms;
|
||||||
|
try {
|
||||||
|
terms = await MatrixClientPeg.get().getTerms(SERVICE_TYPES.IS, fullUrl);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
if (e.cors === "rejected" || e.httpStatus === 404) {
|
||||||
|
terms = null;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return terms && terms["policies"] && (Object.keys(terms["policies"]).length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doesAccountDataHaveIdentityServer() {
|
||||||
|
const event = MatrixClientPeg.get().getAccountData("m.identity_server");
|
||||||
|
return event && event.getContent() && event.getContent()['base_url'];
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue