Merge pull request #2511 from matrix-org/jryans/rm-team-server

Remove support for team servers
This commit is contained in:
J. Ryan Stinnett 2019-01-25 16:48:24 -06:00 committed by GitHub
commit a07ba49641
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 41 additions and 655 deletions

View file

@ -27,7 +27,6 @@ import UserActivity from './UserActivity';
import Presence from './Presence'; import Presence from './Presence';
import dis from './dispatcher'; import dis from './dispatcher';
import DMRoomMap from './utils/DMRoomMap'; import DMRoomMap from './utils/DMRoomMap';
import RtsClient from './RtsClient';
import Modal from './Modal'; import Modal from './Modal';
import sdk from './index'; import sdk from './index';
import ActiveWidgetStore from './stores/ActiveWidgetStore'; import ActiveWidgetStore from './stores/ActiveWidgetStore';
@ -224,7 +223,7 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) {
// //
// The plan is to gradually move the localStorage access done here into // The plan is to gradually move the localStorage access done here into
// SessionStore to avoid bugs where the view becomes out-of-sync with // SessionStore to avoid bugs where the view becomes out-of-sync with
// localStorage (e.g. teamToken, isGuest etc.) // localStorage (e.g. isGuest etc.)
async function _restoreFromLocalStorage() { async function _restoreFromLocalStorage() {
if (!localStorage) { if (!localStorage) {
return false; return false;
@ -286,15 +285,6 @@ function _handleLoadSessionFailure(e) {
}); });
} }
let rtsClient = null;
export function initRtsClient(url) {
if (url) {
rtsClient = new RtsClient(url);
} else {
rtsClient = null;
}
}
/** /**
* Transitions to a logged-in state using the given credentials. * Transitions to a logged-in state using the given credentials.
* *
@ -333,7 +323,7 @@ async function _doSetLoggedIn(credentials, clearStorage) {
); );
// This is dispatched to indicate that the user is still in the process of logging in // This is dispatched to indicate that the user is still in the process of logging in
// because `teamPromise` may take some time to resolve, breaking the assumption that // because async code may take some time to resolve, breaking the assumption that
// `setLoggedIn` takes an "instant" to complete, and dispatch `on_logged_in` a few ms // `setLoggedIn` takes an "instant" to complete, and dispatch `on_logged_in` a few ms
// later than MatrixChat might assume. // later than MatrixChat might assume.
// //
@ -347,10 +337,6 @@ async function _doSetLoggedIn(credentials, clearStorage) {
Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl, credentials.identityServerUrl); Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl, credentials.identityServerUrl);
// Resolves by default
let teamPromise = Promise.resolve(null);
if (localStorage) { if (localStorage) {
try { try {
_persistCredentialsToLocalStorage(credentials); _persistCredentialsToLocalStorage(credentials);
@ -367,27 +353,13 @@ async function _doSetLoggedIn(credentials, clearStorage) {
} catch (e) { } catch (e) {
console.warn("Error using local storage: can't persist session!", e); console.warn("Error using local storage: can't persist session!", e);
} }
if (rtsClient && !credentials.guest) {
teamPromise = rtsClient.login(credentials.userId).then((body) => {
if (body.team_token) {
localStorage.setItem("mx_team_token", body.team_token);
}
return body.team_token;
}, (err) => {
console.warn(`Failed to get team token on login: ${err}` );
return null;
});
}
} else { } else {
console.warn("No local storage available: can't persist session!"); console.warn("No local storage available: can't persist session!");
} }
MatrixClientPeg.replaceUsingCreds(credentials); MatrixClientPeg.replaceUsingCreds(credentials);
teamPromise.then((teamToken) => { dis.dispatch({ action: 'on_logged_in' });
dis.dispatch({action: 'on_logged_in', teamToken: teamToken});
});
await startMatrixClient(); await startMatrixClient();
return MatrixClientPeg.get(); return MatrixClientPeg.get();

View file

@ -1,104 +0,0 @@
import 'whatwg-fetch';
let fetchFunction = fetch;
function checkStatus(response) {
if (!response.ok) {
return response.text().then((text) => {
throw new Error(text);
});
}
return response;
}
function parseJson(response) {
return response.json();
}
function encodeQueryParams(params) {
return '?' + Object.keys(params).map((k) => {
return k + '=' + encodeURIComponent(params[k]);
}).join('&');
}
const request = (url, opts) => {
if (opts && opts.qs) {
url += encodeQueryParams(opts.qs);
delete opts.qs;
}
if (opts && opts.body) {
if (!opts.headers) {
opts.headers = {};
}
opts.body = JSON.stringify(opts.body);
opts.headers['Content-Type'] = 'application/json';
}
return fetchFunction(url, opts)
.then(checkStatus)
.then(parseJson);
};
export default class RtsClient {
constructor(url) {
this._url = url;
}
getTeamsConfig() {
return request(this._url + '/teams');
}
/**
* Track a referral with the Riot Team Server. This should be called once a referred
* user has been successfully registered.
* @param {string} referrer the user ID of one who referred the user to Riot.
* @param {string} sid the sign-up identity server session ID .
* @param {string} clientSecret the sign-up client secret.
* @returns {Promise} a promise that resolves to { team_token: 'sometoken' } upon
* success.
*/
trackReferral(referrer, sid, clientSecret) {
return request(this._url + '/register',
{
body: {
referrer: referrer,
session_id: sid,
client_secret: clientSecret,
},
method: 'POST',
},
);
}
getTeam(teamToken) {
return request(this._url + '/teamConfiguration',
{
qs: {
team_token: teamToken,
},
},
);
}
/**
* Signal to the RTS that a login has occurred and that a user requires their team's
* token.
* @param {string} userId the user ID of the user who is a member of a team.
* @returns {Promise} a promise that resolves to { team_token: 'sometoken' } upon
* success.
*/
login(userId) {
return request(this._url + '/login',
{
qs: {
user_id: userId,
},
},
);
}
// allow fetch to be replaced, for testing.
static setFetch(fn) {
fetchFunction = fn;
}
}

View file

@ -30,11 +30,6 @@ class HomePage extends React.Component {
static displayName = 'HomePage'; static displayName = 'HomePage';
static propTypes = { static propTypes = {
// URL base of the team server. Optional.
teamServerUrl: PropTypes.string,
// Team token. Optional. If set, used to get the static homepage of the team
// associated. If unset, homePageUrl will be used.
teamToken: PropTypes.string,
// URL to use as the iFrame src. Defaults to /home.html. // URL to use as the iFrame src. Defaults to /home.html.
homePageUrl: PropTypes.string, homePageUrl: PropTypes.string,
}; };
@ -56,11 +51,6 @@ class HomePage extends React.Component {
componentWillMount() { componentWillMount() {
this._unmounted = false; this._unmounted = false;
if (this.props.teamToken && this.props.teamServerUrl) {
this.setState({
iframeSrc: `${this.props.teamServerUrl}/static/${this.props.teamToken}/home.html`,
});
} else {
// we use request() to inline the homepage into the react component // we use request() to inline the homepage into the react component
// so that it can inherit CSS and theming easily rather than mess around // so that it can inherit CSS and theming easily rather than mess around
// with iframes and trying to synchronise document.stylesheets. // with iframes and trying to synchronise document.stylesheets.
@ -85,7 +75,6 @@ class HomePage extends React.Component {
}, },
); );
} }
}
componentWillUnmount() { componentWillUnmount() {
this._unmounted = true; this._unmounted = true;

View file

@ -63,7 +63,6 @@ const LoggedInView = React.createClass({
// transitioned to PWLU) // transitioned to PWLU)
onRegistered: PropTypes.func, onRegistered: PropTypes.func,
collapsedRhs: PropTypes.bool, collapsedRhs: PropTypes.bool,
teamToken: PropTypes.string,
// Used by the RoomView to handle joining rooms // Used by the RoomView to handle joining rooms
viaServers: PropTypes.arrayOf(PropTypes.string), viaServers: PropTypes.arrayOf(PropTypes.string),
@ -457,8 +456,6 @@ const LoggedInView = React.createClass({
pageElement = <UserSettings pageElement = <UserSettings
onClose={this.props.onCloseAllSettings} onClose={this.props.onCloseAllSettings}
brand={this.props.config.brand} brand={this.props.config.brand}
referralBaseUrl={this.props.config.referralBaseUrl}
teamToken={this.props.teamToken}
/>; />;
break; break;
@ -475,15 +472,7 @@ const LoggedInView = React.createClass({
case PageTypes.HomePage: case PageTypes.HomePage:
{ {
// If team server config is present, pass the teamServerURL. props.teamToken
// must also be set for the team page to be displayed, otherwise the
// welcomePageUrl is used (which might be undefined).
const teamServerUrl = this.props.config.teamServerConfig ?
this.props.config.teamServerConfig.teamServerURL : null;
pageElement = <HomePage pageElement = <HomePage
teamServerUrl={teamServerUrl}
teamToken={this.props.teamToken}
homePageUrl={this.props.config.welcomePageUrl} homePageUrl={this.props.config.welcomePageUrl}
/>; />;
} }

View file

@ -75,8 +75,8 @@ const VIEWS = {
// we have valid matrix credentials (either via an explicit login, via the // we have valid matrix credentials (either via an explicit login, via the
// initial re-animation/guest registration, or via a registration), and are // initial re-animation/guest registration, or via a registration), and are
// now setting up a matrixclient to talk to it. This isn't an instant // now setting up a matrixclient to talk to it. This isn't an instant
// process because (a) we need to clear out indexeddb, and (b) we need to // process because we need to clear out indexeddb. While it is going on we
// talk to the team server; while it is going on we show a big spinner. // show a big spinner.
LOGGING_IN: 5, LOGGING_IN: 5,
// we are logged in with an active matrix client. // we are logged in with an active matrix client.
@ -256,42 +256,6 @@ export default React.createClass({
MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit; MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit;
} }
// To enable things like riot.im/geektime in a nicer way than rewriting the URL
// and appending a team token query parameter, use the first path segment to
// indicate a team, with "public" team tokens stored in the config teamTokenMap.
let routedTeamToken = null;
if (this.props.config.teamTokenMap) {
const teamName = window.location.pathname.split('/')[1];
if (teamName && this.props.config.teamTokenMap.hasOwnProperty(teamName)) {
routedTeamToken = this.props.config.teamTokenMap[teamName];
}
}
// Persist the team token across refreshes using sessionStorage. A new window or
// tab will not persist sessionStorage, but refreshes will.
if (this.props.startingFragmentQueryParams.team_token) {
window.sessionStorage.setItem(
'mx_team_token',
this.props.startingFragmentQueryParams.team_token,
);
}
// Use the locally-stored team token first, then as a fall-back, check to see if
// a referral link was used, which will contain a query parameter `team_token`.
this._teamToken = routedTeamToken ||
window.localStorage.getItem('mx_team_token') ||
window.sessionStorage.getItem('mx_team_token');
// Some users have ended up with "undefined" as their local storage team token,
// treat that as undefined.
if (this._teamToken === "undefined") {
this._teamToken = undefined;
}
if (this._teamToken) {
console.info(`Team token set to ${this._teamToken}`);
}
// Set up the default URLs (async) // Set up the default URLs (async)
if (this.getDefaultServerName() && !this.getDefaultHsUrl(false)) { if (this.getDefaultServerName() && !this.getDefaultHsUrl(false)) {
this.setState({loadingDefaultHomeserver: true}); this.setState({loadingDefaultHomeserver: true});
@ -360,9 +324,6 @@ export default React.createClass({
linkifyMatrix.onGroupClick = this.onGroupClick; linkifyMatrix.onGroupClick = this.onGroupClick;
} }
const teamServerConfig = this.props.config.teamServerConfig || {};
Lifecycle.initRtsClient(teamServerConfig.teamServerURL);
// the first thing to do is to try the token params in the query-string // the first thing to do is to try the token params in the query-string
Lifecycle.attemptTokenLogin(this.props.realQueryParams).then((loggedIn) => { Lifecycle.attemptTokenLogin(this.props.realQueryParams).then((loggedIn) => {
if (loggedIn) { if (loggedIn) {
@ -726,7 +687,7 @@ export default React.createClass({
}); });
break; break;
case 'on_logged_in': case 'on_logged_in':
this._onLoggedIn(payload.teamToken); this._onLoggedIn();
break; break;
case 'on_logged_out': case 'on_logged_out':
this._onLoggedOut(); this._onLoggedOut();
@ -1196,16 +1157,10 @@ export default React.createClass({
/** /**
* Called when a new logged in session has started * Called when a new logged in session has started
*
* @param {string} teamToken
*/ */
_onLoggedIn: async function(teamToken) { _onLoggedIn: async function() {
this.setStateForNewView({view: VIEWS.LOGGED_IN}); this.setStateForNewView({view: VIEWS.LOGGED_IN});
if (teamToken) { if (this._is_registered) {
// A team member has logged in, not a guest
this._teamToken = teamToken;
dis.dispatch({action: 'view_home_page'});
} else if (this._is_registered) {
this._is_registered = false; this._is_registered = false;
if (this.props.config.welcomeUserId && getCurrentLanguage().startsWith("en")) { if (this.props.config.welcomeUserId && getCurrentLanguage().startsWith("en")) {
@ -1261,7 +1216,6 @@ export default React.createClass({
currentRoomId: null, currentRoomId: null,
page_type: PageTypes.RoomDirectory, page_type: PageTypes.RoomDirectory,
}); });
this._teamToken = null;
this._setPageSubtitle(); this._setPageSubtitle();
}, },
@ -1707,15 +1661,13 @@ export default React.createClass({
onReturnToAppClick: function() { onReturnToAppClick: function() {
// treat it the same as if the user had completed the login // treat it the same as if the user had completed the login
this._onLoggedIn(null); this._onLoggedIn();
}, },
// returns a promise which resolves to the new MatrixClient // returns a promise which resolves to the new MatrixClient
onRegistered: function(credentials, teamToken) { onRegistered: function(credentials) {
// XXX: These both should be in state or ideally store(s) because we risk not // XXX: This should be in state or ideally store(s) because we risk not
// rendering the most up-to-date view of state otherwise. // rendering the most up-to-date view of state otherwise.
// teamToken may not be truthy
this._teamToken = teamToken;
this._is_registered = true; this._is_registered = true;
return Lifecycle.setLoggedIn(credentials); return Lifecycle.setLoggedIn(credentials);
}, },
@ -1888,7 +1840,6 @@ export default React.createClass({
onCloseAllSettings={this.onCloseAllSettings} onCloseAllSettings={this.onCloseAllSettings}
onRegistered={this.onRegistered} onRegistered={this.onRegistered}
currentRoomId={this.state.currentRoomId} currentRoomId={this.state.currentRoomId}
teamToken={this._teamToken}
showCookieBar={this.state.showCookieBar} showCookieBar={this.state.showCookieBar}
{...this.props} {...this.props}
{...this.state} {...this.state}
@ -1929,7 +1880,6 @@ export default React.createClass({
defaultHsUrl={this.getDefaultHsUrl()} defaultHsUrl={this.getDefaultHsUrl()}
defaultIsUrl={this.getDefaultIsUrl()} defaultIsUrl={this.getDefaultIsUrl()}
brand={this.props.config.brand} brand={this.props.config.brand}
teamServerConfig={this.props.config.teamServerConfig}
customHsUrl={this.getCurrentHsUrl()} customHsUrl={this.getCurrentHsUrl()}
customIsUrl={this.getCurrentIsUrl()} customIsUrl={this.getCurrentIsUrl()}
makeRegistrationUrl={this._makeRegistrationUrl} makeRegistrationUrl={this._makeRegistrationUrl}

View file

@ -167,13 +167,6 @@ module.exports = React.createClass({
onClose: PropTypes.func, onClose: PropTypes.func,
// The brand string given when creating email pushers // The brand string given when creating email pushers
brand: PropTypes.string, brand: PropTypes.string,
// The base URL to use in the referral link. Defaults to window.location.origin.
referralBaseUrl: PropTypes.string,
// Team token for the referral link. If falsy, the referral section will
// not appear
teamToken: PropTypes.string,
}, },
getDefaultProps: function() { getDefaultProps: function() {
@ -590,27 +583,6 @@ module.exports = React.createClass({
return <GroupUserSettings />; return <GroupUserSettings />;
}, },
_renderReferral: function() {
const teamToken = this.props.teamToken;
if (!teamToken) {
return null;
}
if (typeof teamToken !== 'string') {
console.warn('Team token not a string');
return null;
}
const href = (this.props.referralBaseUrl || window.location.origin) +
`/#/register?referrer=${this._me}&team_token=${teamToken}`;
return (
<div>
<h3>Referral</h3>
<div className="mx_UserSettings_section">
{ _t("Refer a friend to Riot:") } <a href={href} target="_blank" rel="noopener">{ href }</a>
</div>
</div>
);
},
onLanguageChange: function(newLang) { onLanguageChange: function(newLang) {
if (this.state.language !== newLang) { if (this.state.language !== newLang) {
SettingsStore.setValue("language", null, SettingLevel.DEVICE, newLang); SettingsStore.setValue("language", null, SettingLevel.DEVICE, newLang);
@ -1355,8 +1327,6 @@ module.exports = React.createClass({
{ this._renderGroupSettings() } { this._renderGroupSettings() }
{ this._renderReferral() }
{ notificationArea } { notificationArea }
{ this._renderUserInterfaceSettings() } { this._renderUserInterfaceSettings() }

View file

@ -23,9 +23,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import sdk from '../../../index'; import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import RegistrationForm from '../../views/auth/RegistrationForm'; import RegistrationForm from '../../views/auth/RegistrationForm';
import RtsClient from '../../../RtsClient';
import { _t, _td } from '../../../languageHandler'; import { _t, _td } from '../../../languageHandler';
import SdkConfig from '../../../SdkConfig'; import SdkConfig from '../../../SdkConfig';
import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
@ -48,13 +46,6 @@ module.exports = React.createClass({
brand: PropTypes.string, brand: PropTypes.string,
email: PropTypes.string, email: PropTypes.string,
referrer: PropTypes.string, referrer: PropTypes.string,
teamServerConfig: PropTypes.shape({
// Email address to request new teams
supportEmail: PropTypes.string.isRequired,
// URL of the riot-team-server to get team configurations and track referrals
teamServerURL: PropTypes.string.isRequired,
}),
teamSelected: PropTypes.object,
// The default server name to use when the user hasn't specified // The default server name to use when the user hasn't specified
// one. This is used when displaying the defaultHsUrl in the UI. // one. This is used when displaying the defaultHsUrl in the UI.
@ -70,18 +61,11 @@ module.exports = React.createClass({
onLoginClick: PropTypes.func.isRequired, onLoginClick: PropTypes.func.isRequired,
onCancelClick: PropTypes.func, onCancelClick: PropTypes.func,
onServerConfigChange: PropTypes.func.isRequired, onServerConfigChange: PropTypes.func.isRequired,
rtsClient: PropTypes.shape({
getTeamsConfig: PropTypes.func.isRequired,
trackReferral: PropTypes.func.isRequired,
getTeam: PropTypes.func.isRequired,
}),
}, },
getInitialState: function() { getInitialState: function() {
return { return {
busy: false, busy: false,
teamServerBusy: false,
errorText: null, errorText: null,
// We remember the values entered by the user because // We remember the values entered by the user because
// the registration form will be unmounted during the // the registration form will be unmounted during the
@ -106,37 +90,7 @@ module.exports = React.createClass({
componentWillMount: function() { componentWillMount: function() {
this._unmounted = false; this._unmounted = false;
this._replaceClient(); this._replaceClient();
if (
this.props.teamServerConfig &&
this.props.teamServerConfig.teamServerURL &&
!this._rtsClient
) {
this._rtsClient = this.props.rtsClient || new RtsClient(this.props.teamServerConfig.teamServerURL);
this.setState({
teamServerBusy: true,
});
// GET team configurations including domains, names and icons
this._rtsClient.getTeamsConfig().then((data) => {
const teamsConfig = {
teams: data,
supportEmail: this.props.teamServerConfig.supportEmail,
};
console.log('Setting teams config to ', teamsConfig);
this.setState({
teamsConfig: teamsConfig,
teamServerBusy: false,
});
}, (err) => {
console.error('Error retrieving config for teams', err);
this.setState({
teamServerBusy: false,
});
});
}
}, },
onServerConfigChange: function(config) { onServerConfigChange: function(config) {
@ -191,7 +145,7 @@ module.exports = React.createClass({
}); });
}, },
_onUIAuthFinished: function(success, response, extra) { _onUIAuthFinished: async function(success, response, extra) {
if (!success) { if (!success) {
let msg = response.message || response.toString(); let msg = response.message || response.toString();
// can we give a better error message? // can we give a better error message?
@ -240,58 +194,15 @@ module.exports = React.createClass({
doingUIAuth: false, doingUIAuth: false,
}); });
// Done regardless of `teamSelected`. People registering with non-team emails const cli = await this.props.onLoggedIn({
// will just nop. The point of this being we might not have the email address
// that the user registered with at this stage (depending on whether this
// is the client they initiated registration).
let trackPromise = Promise.resolve(null);
if (this._rtsClient && extra.emailSid) {
// Track referral if this.props.referrer set, get team_token in order to
// retrieve team config and see welcome page etc.
trackPromise = this._rtsClient.trackReferral(
this.props.referrer || '', // Default to empty string = not referred
extra.emailSid,
extra.clientSecret,
).then((data) => {
const teamToken = data.team_token;
// Store for use /w welcome pages
window.localStorage.setItem('mx_team_token', teamToken);
this._rtsClient.getTeam(teamToken).then((team) => {
console.log(
`User successfully registered with team ${team.name}`,
);
if (!team.rooms) {
return;
}
// Auto-join rooms
team.rooms.forEach((room) => {
if (room.auto_join && room.room_id) {
console.log(`Auto-joining ${room.room_id}`);
MatrixClientPeg.get().joinRoom(room.room_id);
}
});
}, (err) => {
console.error('Error getting team config', err);
});
return teamToken;
}, (err) => {
console.error('Error tracking referral', err);
});
}
trackPromise.then((teamToken) => {
return this.props.onLoggedIn({
userId: response.user_id, userId: response.user_id,
deviceId: response.device_id, deviceId: response.device_id,
homeserverUrl: this._matrixClient.getHomeserverUrl(), homeserverUrl: this._matrixClient.getHomeserverUrl(),
identityServerUrl: this._matrixClient.getIdentityServerUrl(), identityServerUrl: this._matrixClient.getIdentityServerUrl(),
accessToken: response.access_token, accessToken: response.access_token,
}, teamToken);
}).then((cli) => {
return this._setupPushers(cli);
}); });
this._setupPushers(cli);
}, },
_setupPushers: function(matrixClient) { _setupPushers: function(matrixClient) {
@ -356,12 +267,6 @@ module.exports = React.createClass({
}); });
}, },
onTeamSelected: function(teamSelected) {
if (!this._unmounted) {
this.setState({ teamSelected });
}
},
onLoginClick: function(ev) { onLoginClick: function(ev) {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
@ -418,7 +323,7 @@ module.exports = React.createClass({
poll={true} poll={true}
/> />
); );
} else if (this.state.busy || this.state.teamServerBusy || !this.state.flows) { } else if (this.state.busy || !this.state.flows) {
registerBody = <Spinner />; registerBody = <Spinner />;
} else { } else {
let serverConfigSection; let serverConfigSection;
@ -443,11 +348,9 @@ module.exports = React.createClass({
defaultPhoneCountry={this.state.formVals.phoneCountry} defaultPhoneCountry={this.state.formVals.phoneCountry}
defaultPhoneNumber={this.state.formVals.phoneNumber} defaultPhoneNumber={this.state.formVals.phoneNumber}
defaultPassword={this.state.formVals.password} defaultPassword={this.state.formVals.password}
teamsConfig={this.state.teamsConfig}
minPasswordLength={MIN_PASSWORD_LENGTH} minPasswordLength={MIN_PASSWORD_LENGTH}
onError={this.onFormValidationFailed} onError={this.onFormValidationFailed}
onRegisterClick={this.onFormSubmit} onRegisterClick={this.onFormSubmit}
onTeamSelected={this.onTeamSelected}
flows={this.state.flows} flows={this.state.flows}
/> />
{ serverConfigSection } { serverConfigSection }
@ -472,12 +375,7 @@ module.exports = React.createClass({
return ( return (
<AuthPage> <AuthPage>
<AuthHeader <AuthHeader />
icon={this.state.teamSelected ?
this.props.teamServerConfig.teamServerURL + "/static/common/" +
this.state.teamSelected.domain + "/icon.png" :
null}
/>
<AuthBody> <AuthBody>
<h2>{ _t('Create your account') }</h2> <h2>{ _t('Create your account') }</h2>
{ registerBody } { registerBody }

View file

@ -46,17 +46,6 @@ module.exports = React.createClass({
defaultPhoneNumber: PropTypes.string, defaultPhoneNumber: PropTypes.string,
defaultUsername: PropTypes.string, defaultUsername: PropTypes.string,
defaultPassword: PropTypes.string, defaultPassword: PropTypes.string,
teamsConfig: PropTypes.shape({
// Email address to request new teams
supportEmail: PropTypes.string,
teams: PropTypes.arrayOf(PropTypes.shape({
// The displayed name of the team
"name": PropTypes.string,
// The domain of team email addresses
"domain": PropTypes.string,
})).required,
}),
minPasswordLength: PropTypes.number, minPasswordLength: PropTypes.number,
onError: PropTypes.func, onError: PropTypes.func,
onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise
@ -75,7 +64,6 @@ module.exports = React.createClass({
getInitialState: function() { getInitialState: function() {
return { return {
fieldValid: {}, fieldValid: {},
selectedTeam: null,
// The ISO2 country code selected in the phone number entry // The ISO2 country code selected in the phone number entry
phoneCountry: this.props.defaultPhoneCountry, phoneCountry: this.props.defaultPhoneCountry,
}; };
@ -150,10 +138,6 @@ module.exports = React.createClass({
return true; return true;
}, },
_isUniEmail: function(email) {
return email.endsWith('.ac.uk') || email.endsWith('.edu') || email.endsWith('matrix.org');
},
validateField: function(fieldID) { validateField: function(fieldID) {
const pwd1 = this.refs.password.value.trim(); const pwd1 = this.refs.password.value.trim();
const pwd2 = this.refs.passwordConfirm.value.trim(); const pwd2 = this.refs.passwordConfirm.value.trim();
@ -161,24 +145,6 @@ module.exports = React.createClass({
switch (fieldID) { switch (fieldID) {
case FIELD_EMAIL: { case FIELD_EMAIL: {
const email = this.refs.email.value; const email = this.refs.email.value;
if (this.props.teamsConfig && this._isUniEmail(email)) {
const matchingTeam = this.props.teamsConfig.teams.find(
(team) => {
return email.split('@').pop() === team.domain;
},
) || null;
this.setState({
selectedTeam: matchingTeam,
showSupportEmail: !matchingTeam,
});
this.props.onTeamSelected(matchingTeam);
} else {
this.props.onTeamSelected(null);
this.setState({
selectedTeam: null,
showSupportEmail: false,
});
}
const emailValid = email === '' || Email.looksValid(email); const emailValid = email === '' || Email.looksValid(email);
if (this._authStepIsRequired('m.login.email.identity') && (!emailValid || email === '')) { if (this._authStepIsRequired('m.login.email.identity') && (!emailValid || email === '')) {
this.markFieldValid(fieldID, false, "RegistrationForm.ERR_MISSING_EMAIL"); this.markFieldValid(fieldID, false, "RegistrationForm.ERR_MISSING_EMAIL");
@ -304,30 +270,6 @@ module.exports = React.createClass({
value={self.state.email} /> value={self.state.email} />
</div> </div>
); );
let belowEmailSection;
if (this.props.teamsConfig) {
if (this.props.teamsConfig.supportEmail && this.state.showSupportEmail) {
belowEmailSection = (
<p className="mx_Login_support">
Sorry, but your university is not registered with us just yet.&nbsp;
Email us on&nbsp;
<a href={"mailto:" + this.props.teamsConfig.supportEmail}>
{ this.props.teamsConfig.supportEmail }
</a>&nbsp;
to get your university signed up.
Or continue to register with Riot to enjoy our open source platform.
</p>
);
} else if (this.state.selectedTeam) {
belowEmailSection = (
<p className="mx_Login_support">
{_t("You are registering with %(SelectedTeamName)s", {
SelectedTeamName: this.state.selectedTeam.name,
})}
</p>
);
}
}
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown'); const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
let phoneSection; let phoneSection;
@ -369,7 +311,6 @@ module.exports = React.createClass({
<div> <div>
<form onSubmit={this.onSubmit}> <form onSubmit={this.onSubmit}>
{ emailSection } { emailSection }
{ belowEmailSection }
{ phoneSection } { phoneSection }
<input type="text" ref="username" <input type="text" ref="username"
placeholder={placeholderUserName} defaultValue={this.props.defaultUsername} placeholder={placeholderUserName} defaultValue={this.props.defaultUsername}

View file

@ -193,9 +193,6 @@ export default React.createClass({
return; return;
} }
// XXX Implement RTS /register here
const teamToken = null;
this.props.onFinished(true, { this.props.onFinished(true, {
userId: response.user_id, userId: response.user_id,
deviceId: response.device_id, deviceId: response.device_id,
@ -203,7 +200,6 @@ export default React.createClass({
identityServerUrl: this._matrixClient.getIdentityServerUrl(), identityServerUrl: this._matrixClient.getIdentityServerUrl(),
accessToken: response.access_token, accessToken: response.access_token,
password: this._generatedPassword, password: this._generatedPassword,
teamToken: teamToken,
}); });
}, },

View file

@ -15,7 +15,6 @@ limitations under the License.
*/ */
import Skinner from './Skinner'; import Skinner from './Skinner';
import RtsClient from './RtsClient';
module.exports.loadSkin = function(skinObject) { module.exports.loadSkin = function(skinObject) {
Skinner.load(skinObject); Skinner.load(skinObject);
@ -28,7 +27,3 @@ module.exports.resetSkin = function() {
module.exports.getComponent = function(componentName) { module.exports.getComponent = function(componentName) {
return Skinner.getComponent(componentName); return Skinner.getComponent(componentName);
}; };
module.exports.setFetch = function(fetchFunction) {
RtsClient.setFetch(fetchFunction);
};

View file

@ -1,105 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const jest = require('jest-mock');
const React = require('react');
const ReactTestUtils = require('react-addons-test-utils');
const expect = require('expect');
const testUtils = require('test-utils');
const sdk = require('matrix-react-sdk');
const Registration = sdk.getComponent('structures.auth.Registration');
let rtsClient;
let client;
const TEAM_CONFIG = {
supportEmail: 'support@some.domain',
teamServerURL: 'http://someteamserver.bla',
};
const CREDENTIALS = {userId: '@me:here'};
const MOCK_REG_RESPONSE = {
user_id: CREDENTIALS.userId,
device_id: 'mydevice',
access_token: '2234569864534231',
};
describe('Registration', function() {
beforeEach(function() {
testUtils.beforeEach(this);
client = testUtils.createTestClient();
client.credentials = CREDENTIALS;
// Mock an RTS client that supports one team and naively returns team tokens when
// tracking by mapping email SIDs to team tokens. This is fine because we only
// want to assert the client behaviour such that a user recognised by the
// rtsClient (which would normally talk to the RTS server) as a team member is
// correctly logged in as one (and other such assertions).
rtsClient = testUtils.createTestRtsClient(
{
'myawesometeam123': {
name: 'Team Awesome',
domain: 'team.awesome.net',
},
},
{'someEmailSid1234': 'myawesometeam123'},
);
});
it('should track a referral following successful registration of a team member', function(done) {
const expectedCreds = {
userId: MOCK_REG_RESPONSE.user_id,
deviceId: MOCK_REG_RESPONSE.device_id,
homeserverUrl: client.getHomeserverUrl(),
identityServerUrl: client.getIdentityServerUrl(),
accessToken: MOCK_REG_RESPONSE.access_token,
};
const onLoggedIn = function(creds, teamToken) {
expect(creds).toEqual(expectedCreds);
expect(teamToken).toBe('myawesometeam123');
done();
};
const res = ReactTestUtils.renderIntoDocument(
<Registration
teamServerConfig={TEAM_CONFIG}
onLoggedIn={onLoggedIn}
rtsClient={rtsClient}
/>,
);
res._onUIAuthFinished(true, MOCK_REG_RESPONSE, {emailSid: 'someEmailSid1234'});
});
it('should NOT track a referral following successful registration of a non-team member', function(done) {
const onLoggedIn = jest.fn(function(creds, teamToken) {
expect(teamToken).toBeFalsy();
done();
});
const res = ReactTestUtils.renderIntoDocument(
<Registration
teamServerConfig={TEAM_CONFIG}
onLoggedIn={onLoggedIn}
rtsClient={rtsClient}
/>,
);
res._onUIAuthFinished(true, MOCK_REG_RESPONSE, {emailSid: 'someOtherEmailSid11'});
});
});

View file

@ -1,91 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const jest = require('jest-mock');
const React = require('react');
const ReactTestUtils = require('react-addons-test-utils');
const expect = require('expect');
const testUtils = require('test-utils');
const sdk = require('matrix-react-sdk');
const RegistrationForm = sdk.getComponent('views.auth.RegistrationForm');
const TEAM_CONFIG = {
supportEmail: "support@some.domain",
teams: [
{ name: "The Team Org.", domain: "team.ac.uk" },
{ name: "The Super Team", domain: "superteam.ac.uk" },
],
};
function doInputEmail(inputEmail, onTeamSelected) {
const res = ReactTestUtils.renderIntoDocument(
<RegistrationForm
teamsConfig={TEAM_CONFIG}
onTeamSelected={onTeamSelected}
flows={[
{
stages: ['m.login.dummy'],
},
]}
/>,
);
const teamInput = res.refs.email;
teamInput.value = inputEmail;
ReactTestUtils.Simulate.change(teamInput);
ReactTestUtils.Simulate.blur(teamInput);
return res;
}
function expectTeamSelectedFromEmailInput(inputEmail, expectedTeam) {
const onTeamSelected = jest.fn();
doInputEmail(inputEmail, onTeamSelected);
expect(onTeamSelected).toHaveBeenCalledWith(expectedTeam);
}
function expectSupportFromEmailInput(inputEmail, isSupportShown) {
const onTeamSelected = jest.fn();
const res = doInputEmail(inputEmail, onTeamSelected);
expect(res.state.showSupportEmail).toBe(isSupportShown);
}
describe('RegistrationForm', function() {
beforeEach(function() {
testUtils.beforeEach(this);
});
it('should select a team when a team email is entered', function() {
expectTeamSelectedFromEmailInput("member@team.ac.uk", TEAM_CONFIG.teams[0]);
});
it('should not select a team when an unrecognised team email is entered', function() {
expectTeamSelectedFromEmailInput("member@someunknownteam.ac.uk", null);
});
it('should show support when an unrecognised team email is entered', function() {
expectSupportFromEmailInput("member@someunknownteam.ac.uk", true);
});
it('should NOT show support when an unrecognised non-team email is entered', function() {
expectSupportFromEmailInput("someone@yahoo.com", false);
});
});

View file

@ -104,20 +104,6 @@ export function createTestClient() {
}; };
} }
export function createTestRtsClient(teamMap, sidMap) {
return {
getTeamsConfig() {
return Promise.resolve(Object.keys(teamMap).map((token) => teamMap[token]));
},
trackReferral(referrer, emailSid, clientSecret) {
return Promise.resolve({team_token: sidMap[emailSid]});
},
getTeam(teamToken) {
return Promise.resolve(teamMap[teamToken]);
},
};
}
/** /**
* Create an Event. * Create an Event.
* @param {Object} opts Values for the event. * @param {Object} opts Values for the event.