From 5f8108d8ff2eebf700879def198648562ea1445e Mon Sep 17 00:00:00 2001
From: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
Date: Thu, 20 Feb 2020 17:08:20 -0300
Subject: [PATCH] [IMPROVEMENT] Honor Register/Login settings (#1727)

Co-authored-by: Diego Mello <diegolmello@gmail.com>
---
 app/constants/settings.js      |  9 ++++
 app/lib/methods/getSettings.js | 17 +++++++
 app/lib/rocketchat.js          |  5 +-
 app/sagas/selectServer.js      |  3 +-
 app/views/LoginSignupView.js   | 88 +++++++++++++++++++++++++---------
 app/views/LoginView.js         | 39 ++++++++++-----
 6 files changed, 124 insertions(+), 37 deletions(-)

diff --git a/app/constants/settings.js b/app/constants/settings.js
index 4b54b400e..4f2c1f444 100644
--- a/app/constants/settings.js
+++ b/app/constants/settings.js
@@ -35,6 +35,15 @@ export default {
 	Accounts_PasswordReset: {
 		type: 'valueAsBoolean'
 	},
+	Accounts_RegistrationForm: {
+		type: 'valueAsString'
+	},
+	Accounts_RegistrationForm_LinkReplacementText: {
+		type: 'valueAsString'
+	},
+	Accounts_ShowFormLogin: {
+		type: 'valueAsBoolean'
+	},
 	CROWD_Enable: {
 		type: 'valueAsBoolean'
 	},
diff --git a/app/lib/methods/getSettings.js b/app/lib/methods/getSettings.js
index 997e3c533..5ab7aa59a 100644
--- a/app/lib/methods/getSettings.js
+++ b/app/lib/methods/getSettings.js
@@ -52,6 +52,23 @@ const serverInfoUpdate = async(serverInfo, iconSetting) => {
 	});
 };
 
+export function getSetting({ server, setting }) {
+	return new Promise(async(resolve, reject) => {
+		try {
+			const result = await fetch(`${ server }/api/v1/settings.public?query={"_id":{"$in":["${ setting }"]}}`).then(response => response.json());
+
+			if (result.success && result.settings.length) {
+				const [{ value }] = result.settings;
+				return resolve(value);
+			}
+		} catch (e) {
+			log(e);
+		}
+
+		return reject();
+	});
+}
+
 export async function setSettings() {
 	const db = database.active;
 	const settingsCollection = db.collections.get('settings');
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 4d5de613c..0805ed58c 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -24,7 +24,7 @@ import subscribeRooms from './methods/subscriptions/rooms';
 
 import protectedFunction from './methods/helpers/protectedFunction';
 import readMessages from './methods/readMessages';
-import getSettings, { setSettings } from './methods/getSettings';
+import getSettings, { getSetting, setSettings } from './methods/getSettings';
 
 import getRooms from './methods/getRooms';
 import getPermissions from './methods/getPermissions';
@@ -620,6 +620,7 @@ const RocketChat = {
 	cancelUpload,
 	isUploadActive,
 	getSettings,
+	getSetting,
 	setSettings,
 	getPermissions,
 	getCustomEmojis,
@@ -911,7 +912,7 @@ const RocketChat = {
 			let loginServices = [];
 			const loginServicesResult = await fetch(`${ server }/api/v1/settings.oauth`).then(response => response.json());
 
-			if (loginServicesResult.success && loginServicesResult.services.length > 0) {
+			if (loginServicesResult.success && loginServicesResult.services) {
 				const { services } = loginServicesResult;
 				loginServices = services;
 
diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js
index 5cfa51ad2..c939868f5 100644
--- a/app/sagas/selectServer.js
+++ b/app/sagas/selectServer.js
@@ -129,8 +129,9 @@ const handleServerRequest = function* handleServerRequest({ server, certificate
 		const serverInfo = yield getServerInfo({ server });
 
 		if (serverInfo) {
+			const showFormLogin = yield RocketChat.getSetting({ server, setting: 'Accounts_ShowFormLogin' });
 			const loginServicesLength = yield RocketChat.getLoginServices(server);
-			if (loginServicesLength === 0) {
+			if (loginServicesLength === 0 && showFormLogin) {
 				Navigation.navigate('LoginView');
 			} else {
 				Navigation.navigate('LoginSignupView');
diff --git a/app/views/LoginSignupView.js b/app/views/LoginSignupView.js
index c2711775a..d3c5bb288 100644
--- a/app/views/LoginSignupView.js
+++ b/app/views/LoginSignupView.js
@@ -58,6 +58,11 @@ const styles = StyleSheet.create({
 	serviceName: {
 		...sharedStyles.textBold
 	},
+	registerDisabled: {
+		...sharedStyles.textRegular,
+		...sharedStyles.textAlignCenter,
+		fontSize: 16
+	},
 	servicesTogglerContainer: {
 		flexDirection: 'row',
 		alignItems: 'center',
@@ -108,6 +113,9 @@ class LoginSignupView extends React.Component {
 		Gitlab_URL: PropTypes.string,
 		CAS_enabled: PropTypes.bool,
 		CAS_login_url: PropTypes.string,
+		Accounts_ShowFormLogin: PropTypes.bool,
+		Accounts_RegistrationForm: PropTypes.string,
+		Accounts_RegistrationForm_LinkReplacementText: PropTypes.string,
 		theme: PropTypes.string
 	}
 
@@ -124,7 +132,7 @@ class LoginSignupView extends React.Component {
 	shouldComponentUpdate(nextProps, nextState) {
 		const { collapsed, servicesHeight } = this.state;
 		const {
-			server, Site_Name, services, theme
+			server, Site_Name, services, Accounts_ShowFormLogin, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, theme
 		} = this.props;
 		if (nextState.collapsed !== collapsed) {
 			return true;
@@ -141,6 +149,15 @@ class LoginSignupView extends React.Component {
 		if (nextProps.theme !== theme) {
 			return true;
 		}
+		if (nextProps.Accounts_ShowFormLogin !== Accounts_ShowFormLogin) {
+			return true;
+		}
+		if (nextProps.Accounts_RegistrationForm !== Accounts_RegistrationForm) {
+			return true;
+		}
+		if (nextProps.Accounts_RegistrationForm_LinkReplacementText !== Accounts_RegistrationForm_LinkReplacementText) {
+			return true;
+		}
 		if (!equal(nextProps.services, services)) {
 			return true;
 		}
@@ -333,10 +350,12 @@ class LoginSignupView extends React.Component {
 
 	renderServicesSeparator = () => {
 		const { collapsed } = this.state;
-		const { services, theme } = this.props;
+		const {
+			services, theme, Accounts_ShowFormLogin, Accounts_RegistrationForm
+		} = this.props;
 		const { length } = Object.values(services);
 
-		if (length > 3) {
+		if (length > 3 && Accounts_ShowFormLogin && Accounts_RegistrationForm) {
 			return (
 				<View style={styles.servicesTogglerContainer}>
 					<View style={[styles.separatorLine, styles.separatorLineLeft, { backgroundColor: themes[theme].auxiliaryText }]} />
@@ -358,6 +377,7 @@ class LoginSignupView extends React.Component {
 		let { name } = service;
 		name = name === 'meteor-developer' ? 'meteor' : name;
 		const icon = `icon_${ name }`;
+		const isSaml = service.service === 'saml';
 		let onPress = () => {};
 
 		switch (service.authType) {
@@ -383,8 +403,8 @@ class LoginSignupView extends React.Component {
 		name = name.charAt(0).toUpperCase() + name.slice(1);
 		const { CAS_enabled, theme } = this.props;
 		let buttonText;
-		if (service.service === 'saml' || (service.service === 'cas' && CAS_enabled)) {
-			buttonText = <Text style={styles.serviceName}>{name}</Text>;
+		if (isSaml || (service.service === 'cas' && CAS_enabled)) {
+			buttonText = <Text style={[styles.serviceName, isSaml && { color: service.buttonLabelColor }]}>{name}</Text>;
 		} else {
 			buttonText = (
 				<>
@@ -396,7 +416,7 @@ class LoginSignupView extends React.Component {
 			<Touch
 				key={service.name}
 				onPress={onPress}
-				style={styles.serviceButton}
+				style={[styles.serviceButton, isSaml && { backgroundColor: service.buttonColor }]}
 				theme={theme}
 			>
 				<View style={[styles.serviceButtonContainer, { borderColor: themes[theme].borderColor }]}>
@@ -409,15 +429,14 @@ class LoginSignupView extends React.Component {
 
 	renderServices = () => {
 		const { servicesHeight } = this.state;
-		const { services } = this.props;
+		const { services, Accounts_ShowFormLogin, Accounts_RegistrationForm } = this.props;
 		const { length } = Object.values(services);
 		const style = {
 			overflow: 'hidden',
 			height: servicesHeight
 		};
 
-
-		if (length > 3) {
+		if (length > 3 && Accounts_ShowFormLogin && Accounts_RegistrationForm) {
 			return (
 				<Animated.View style={style}>
 					{Object.values(services).map(service => this.renderItem(service))}
@@ -431,6 +450,38 @@ class LoginSignupView extends React.Component {
 		);
 	}
 
+	renderLogin = () => {
+		const { Accounts_ShowFormLogin, theme } = this.props;
+		if (!Accounts_ShowFormLogin) {
+			return null;
+		}
+		return (
+			<Button
+				title={<Text>{I18n.t('Login_with')} <Text style={{ ...sharedStyles.textBold }}>{I18n.t('email')}</Text></Text>}
+				type='primary'
+				onPress={() => this.login()}
+				theme={theme}
+				testID='welcome-view-login'
+			/>
+		);
+	}
+
+	renderRegister = () => {
+		const { Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, theme } = this.props;
+		if (Accounts_RegistrationForm !== 'Public') {
+			return <Text style={[styles.registerDisabled, { color: themes[theme].auxiliaryText }]}>{Accounts_RegistrationForm_LinkReplacementText}</Text>;
+		}
+		return (
+			<Button
+				title={I18n.t('Create_account')}
+				type='secondary'
+				onPress={() => this.register()}
+				theme={theme}
+				testID='welcome-view-register'
+			/>
+		);
+	}
+
 	render() {
 		const { theme } = this.props;
 		return (
@@ -452,20 +503,8 @@ class LoginSignupView extends React.Component {
 					<StatusBar theme={theme} />
 					{this.renderServices()}
 					{this.renderServicesSeparator()}
-					<Button
-						title={<Text>{I18n.t('Login_with')} <Text style={{ ...sharedStyles.textBold }}>{I18n.t('email')}</Text></Text>}
-						type='primary'
-						onPress={() => this.login()}
-						theme={theme}
-						testID='welcome-view-login'
-					/>
-					<Button
-						title={I18n.t('Create_account')}
-						type='secondary'
-						onPress={() => this.register()}
-						theme={theme}
-						testID='welcome-view-register'
-					/>
+					{this.renderLogin()}
+					{this.renderRegister()}
 				</ScrollView>
 			</SafeAreaView>
 		);
@@ -478,6 +517,9 @@ const mapStateToProps = state => ({
 	Gitlab_URL: state.settings.API_Gitlab_URL,
 	CAS_enabled: state.settings.CAS_enabled,
 	CAS_login_url: state.settings.CAS_login_url,
+	Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin,
+	Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm,
+	Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText,
 	services: state.login.services
 });
 
diff --git a/app/views/LoginView.js b/app/views/LoginView.js
index a958d44e9..76ff77767 100644
--- a/app/views/LoginView.js
+++ b/app/views/LoginView.js
@@ -29,6 +29,11 @@ const styles = StyleSheet.create({
 		alignItems: 'center',
 		marginTop: 10
 	},
+	registerDisabled: {
+		...sharedStyles.textRegular,
+		...sharedStyles.textAlignCenter,
+		fontSize: 13
+	},
 	dontHaveAccount: {
 		...sharedStyles.textRegular,
 		fontSize: 13
@@ -61,6 +66,8 @@ class LoginView extends React.Component {
 		Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
 		Accounts_PasswordPlaceholder: PropTypes.string,
 		Accounts_PasswordReset: PropTypes.bool,
+		Accounts_RegistrationForm: PropTypes.string,
+		Accounts_RegistrationForm_LinkReplacementText: PropTypes.string,
 		isFetching: PropTypes.bool,
 		failure: PropTypes.bool,
 		theme: PropTypes.string
@@ -101,7 +108,7 @@ class LoginView extends React.Component {
 			user, password, code, showTOTP
 		} = this.state;
 		const {
-			isFetching, failure, error, Site_Name, Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, theme
+			isFetching, failure, error, Site_Name, Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, theme
 		} = this.props;
 		if (nextState.user !== user) {
 			return true;
@@ -133,6 +140,12 @@ class LoginView extends React.Component {
 		if (nextProps.Accounts_PasswordPlaceholder !== Accounts_PasswordPlaceholder) {
 			return true;
 		}
+		if (nextProps.Accounts_RegistrationForm !== Accounts_RegistrationForm) {
+			return true;
+		}
+		if (nextProps.Accounts_RegistrationForm_LinkReplacementText !== Accounts_RegistrationForm_LinkReplacementText) {
+			return true;
+		}
 		if (!equal(nextProps.error, error)) {
 			return true;
 		}
@@ -225,7 +238,7 @@ class LoginView extends React.Component {
 
 	renderUserForm = () => {
 		const {
-			Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_PasswordReset, isFetching, theme
+			Accounts_EmailOrUsernamePlaceholder, Accounts_PasswordPlaceholder, Accounts_PasswordReset, Accounts_RegistrationForm, Accounts_RegistrationForm_LinkReplacementText, isFetching, theme
 		} = this.props;
 		return (
 			<SafeAreaView
@@ -283,15 +296,17 @@ class LoginView extends React.Component {
 						theme={theme}
 					/>
 				)}
-				<View style={styles.bottomContainer}>
-					<Text style={[styles.dontHaveAccount, { color: themes[theme].auxiliaryText }]}>{I18n.t('Dont_Have_An_Account')}</Text>
-					<Text
-						style={[styles.createAccount, { color: themes[theme].actionTintColor }]}
-						onPress={this.register}
-						testID='login-view-register'
-					>{I18n.t('Create_account')}
-					</Text>
-				</View>
+				{Accounts_RegistrationForm === 'Public' ? (
+					<View style={styles.bottomContainer}>
+						<Text style={[styles.dontHaveAccount, { color: themes[theme].auxiliaryText }]}>{I18n.t('Dont_Have_An_Account')}</Text>
+						<Text
+							style={[styles.createAccount, { color: themes[theme].actionTintColor }]}
+							onPress={this.register}
+							testID='login-view-register'
+						>{I18n.t('Create_account')}
+						</Text>
+					</View>
+				) : (<Text style={[styles.registerDisabled, { color: themes[theme].auxiliaryText }]}>{Accounts_RegistrationForm_LinkReplacementText}</Text>)}
 			</SafeAreaView>
 		);
 	}
@@ -323,6 +338,8 @@ const mapStateToProps = state => ({
 	Site_Name: state.settings.Site_Name,
 	Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
 	Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
+	Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm,
+	Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText,
 	Accounts_PasswordReset: state.settings.Accounts_PasswordReset
 });
 
-- 
GitLab