Skip to content

Commit

Permalink
feat: Implement adminRestrictedOperation config to support disabling …
Browse files Browse the repository at this point in the history
…multiple providers (#857)

* Implement adminRestrictedOpeartion config to support disabling multiple providers.
  • Loading branch information
xil222 authored Jul 15, 2021
1 parent 7df4278 commit 271f012
Show file tree
Hide file tree
Showing 81 changed files with 8,518 additions and 4,676 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Improved UI support for all identity provider sign up operations when they are restricted by site administrators.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,30 @@ FirebaseUI supports the following configuration parameters.
when Privacy Policy link is clicked.
</td>
</tr>
<tr>
<td>adminRestrictedOperation</td>
<td>No</td>
<td>
The object for configuring `adminRestrictedOperation` options, contains 3
fields:
`status(boolean)`: This flag should mirror the project user actions
("Enable create") settings. When sign-up is disabled in the project settings,
this should be set to `true`. Setting this to `true` without disabling sign-up
in the project settings will not have any effect. For GCIP projects, this is
done by going to the "Settings" page in the "Identity Platform" section in the
Cloud Console. Under the "USERS" tab, go to "User actions". Uncheck "Enable
create (sign-up)" and click "SAVE".
This does not enforce the policy but is rather useful for providing additional
instructions to the end user when a user tries to create a new user account
and the Auth server blocks the operation. This boolean works on all providers
(federated, email/password, email link and phone number).
`adminEmail(string|undefined)`: The optional site administrator email to
contact for access when sign up is disabled, for example: `[email protected]`.
`helpLink(string|undefined)`: The optional help link to provide information
on how to get access to the site when sign up is disabled. For example:
`https://www.example.com/trouble_signing_in`.
</td>
</tr>
</tbody>
</table>

Expand Down
19 changes: 16 additions & 3 deletions demo/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@ function getUiConfig() {
'privacyPolicyUrl': 'https://www.google.com',
'credentialHelper': CLIENT_ID && CLIENT_ID != 'YOUR_OAUTH_CLIENT_ID' ?
firebaseui.auth.CredentialHelper.GOOGLE_YOLO :
firebaseui.auth.CredentialHelper.NONE
firebaseui.auth.CredentialHelper.NONE,
'adminRestrictedOperation': {
status: getAdminRestrictedOperationStatus()
}
};
}

Expand All @@ -101,7 +104,9 @@ ui.disableAutoSignIn();
*/
function getWidgetUrl() {
return '/widget#recaptcha=' + getRecaptchaMode() + '&emailSignInMethod=' +
getEmailSignInMethod();
getEmailSignInMethod() + '&disableEmailSignUpStatus=' +
getDisableSignUpStatus() + '&adminRestrictedOperationStatus=' +
getAdminRestrictedOperationStatus();
}


Expand Down Expand Up @@ -196,10 +201,14 @@ function handleConfigChange() {
'input[name="emailSignInMethod"]:checked').value;
var currentDisableSignUpStatus =
document.getElementById("email-disableSignUp-status").checked;
var currentAdminRestrictedOperationStatus =
document.getElementById("admin-restricted-operation-status").checked;
location.replace(
location.pathname + '#recaptcha=' + newRecaptchaValue +
'&emailSignInMethod=' + newEmailSignInMethodValue +
'&disableEmailSignUpStatus=' + currentDisableSignUpStatus);
'&disableEmailSignUpStatus=' + currentDisableSignUpStatus +
'&adminRestrictedOperationStatus=' +
currentAdminRestrictedOperationStatus);
// Reset the inline widget so the config changes are reflected.
ui.reset();
ui.start('#firebaseui-container', getUiConfig());
Expand Down Expand Up @@ -243,6 +252,10 @@ var initApp = function() {
'change', handleConfigChange);
document.getElementById("email-disableSignUp-status").checked =
getDisableSignUpStatus();
document.getElementById('admin-restricted-operation-status').addEventListener(
'change', handleConfigChange);
document.getElementById("admin-restricted-operation-status").checked =
getAdminRestrictedOperationStatus();
};

window.addEventListener('load', initApp);
9 changes: 9 additions & 0 deletions demo/public/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ function getDisableSignUpStatus() {
}


/**
* @return {boolean} The admin restricted operation status from the configuration.
*/
function getAdminRestrictedOperationStatus() {
var config = parseQueryString(location.hash);
return config['adminRestrictedOperationStatus'] === 'true';
}


/**
* @param {string} queryString The full query string.
* @return {!Object<string, string>} The parsed query parameters.
Expand Down
2 changes: 2 additions & 0 deletions demo/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ <h4>You are signed out.</h4>
</label><br>
<label for="disableEmailSignUpStatus">Disable email sign up:</label>
<input type="checkbox" id="email-disableSignUp-status" name="disableEmailSignUpStatus">
<label for="adminRestrictedOperationStatus">Admin restricted operation status:</label>
<input type="checkbox" id="admin-restricted-operation-status" name="adminRestrictedOperationStatus">
</fieldset>
<p>
<button id="sign-in-with-redirect">Sign In with Redirect</button>
Expand Down
5 changes: 4 additions & 1 deletion demo/public/widget.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@
'tosUrl': 'https://www.google.com',
'credentialHelper': CLIENT_ID && CLIENT_ID != 'YOUR_OAUTH_CLIENT_ID' ?
firebaseui.auth.CredentialHelper.GOOGLE_YOLO :
firebaseui.auth.CredentialHelper.NONE
firebaseui.auth.CredentialHelper.NONE,
'adminRestrictedOperation': {
status: getAdminRestrictedOperationStatus()
}
};

// Initialize the FirebaseUI Widget using Firebase.
Expand Down
19 changes: 16 additions & 3 deletions externs/firebaseui-externs.js
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,18 @@ firebaseui.auth.Config.prototype.privacyPolicyUrl;
*/
firebaseui.auth.Config.prototype.widgetUrl;

/**
* The configuration mirroring the project user actions ("Enable create")
* settings. When sign-up is disabled in the project settings, this
* configuration should be provided with the status field set to `true`. This
* does not enforce the policy but is rather useful for providing additional
* instructions to the end user when a user tries to create a new user account
* and the Auth server blocks the operation.
*
* @type {firebaseui.auth.DisableSignUpConfig|undefined}
*/
firebaseui.auth.Config.prototype.adminRestrictedOperation;


/**
* The tenant level CIAP configuration settings.
Expand Down Expand Up @@ -931,15 +943,16 @@ firebaseui.auth.OidcSignInOption.prototype.customParameters;


/**
* Defines configurations for disabling email sign up.
* Defines the configuration for how to handle errors associated with disabling
* users from signing up using FirebaseUI.
*
* @interface
*/
firebaseui.auth.DisableSignUpConfig = function() {};

/**
* Whether new user sign up with email/password or email link is disabled.
* The default is false.
* Whether a new user is unable to sign up in FirebaseUI. This is true when a
* new user cannot sign up, false otherwise.
*
* @type {boolean}
*/
Expand Down
22 changes: 8 additions & 14 deletions javascript/data/country_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ goog.setTestOnly('firebaseui.auth.data.countryTest');

goog.require('firebaseui.auth.data.country');
goog.require('firebaseui.auth.data.country.LookupTree');
goog.require('goog.array');
goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');
goog.require('goog.userAgent.product');
Expand All @@ -33,16 +32,11 @@ goog.require('goog.userAgent.product');
* @param {string} locale
*/
function assertSortCountryListForLocale(sortedNames, inputNames, locale) {
var countryList = goog.array.map(inputNames, function(name) {
return {
name: name,
e164_key: '',
e164_cc: '',
iso2_cc: ''
};
var countryList = inputNames.map(function(name) {
return {name: name, e164_key: '', e164_cc: '', iso2_cc: ''};
});
firebaseui.auth.data.country.sortCountryListForLocale(countryList, locale);
var actualSortedNames = goog.array.map(countryList, function(country) {
var actualSortedNames = countryList.map(function(country) {
return country.name;
});
assertArrayEquals(sortedNames, actualSortedNames);
Expand All @@ -55,7 +49,7 @@ function assertSortCountryListForLocale(sortedNames, inputNames, locale) {
* @return {!Array<string>} List of country iso2_cc names.
*/
function getCountryCodesForList(countries) {
return goog.array.map(countries, function(country) {
return countries.map(function(country) {
return country.iso2_cc;
});
}
Expand Down Expand Up @@ -158,7 +152,7 @@ function testGetCountriesByIso2() {
*/
function testGetCountriesByIso2_multipleMatches() {
var countries = firebaseui.auth.data.country.getCountriesByIso2('xk');
var actualCodes = goog.array.map(countries, function(country) {
var actualCodes = countries.map(function(country) {
return country.e164_cc;
});
assertSameElements(['377', '381', '386'], actualCodes);
Expand All @@ -185,7 +179,7 @@ function testGetCountriesByE164Code() {
*/
function testGetCountriesByE164Code_multipleMatches() {
var countries = firebaseui.auth.data.country.getCountriesByE164Code('44');
var actualKeys = goog.array.map(countries, function(country) {
var actualKeys = countries.map(function(country) {
return country.e164_key;
});
assertSameElements(['44-GG-0', '44-IM-0', '44-JE-0', '44-GB-0'], actualKeys);
Expand Down Expand Up @@ -219,7 +213,7 @@ function testGetCountriesByE164eOrIsoCode_iso() {
function testGetCountriesByE164OrIsoCode_e164_multipleMatches() {
var countries =
firebaseui.auth.data.country.getCountriesByE164OrIsoCode('+44');
var actualKeys = goog.array.map(countries, function(country) {
var actualKeys = countries.map(function(country) {
return country.e164_key;
});
assertSameElements(['44-GG-0', '44-IM-0', '44-JE-0', '44-GB-0'], actualKeys);
Expand All @@ -229,7 +223,7 @@ function testGetCountriesByE164OrIsoCode_e164_multipleMatches() {
function testGetCountriesByE164OrIsoCode_iso_multipleMatches() {
var countries =
firebaseui.auth.data.country.getCountriesByE164OrIsoCode('xk');
var actualKeys = goog.array.map(countries, function(country) {
var actualKeys = countries.map(function(country) {
return country.e164_key;
});
assertSameElements(['377-XK-0', '381-XK-0', '386-XK-0'], actualKeys);
Expand Down
5 changes: 2 additions & 3 deletions javascript/ui/element/phoneconfirmationcodetesthelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ goog.setTestOnly('firebaseui.auth.ui.element.PhoneConfirmationCodeTestHelper');
goog.require('firebaseui.auth.ui.element');
/** @suppress {extraRequire} Required for test helpers. */
goog.require('firebaseui.auth.ui.element.ElementTestHelper');
goog.require('goog.array');
goog.require('goog.dom.forms');
goog.require('goog.events.KeyCodes');

Expand Down Expand Up @@ -75,7 +74,7 @@ element.PhoneConfirmationCodeTestHelper.prototype
'1234567', // too long
'a12356', // char
];
goog.array.forEach(invalidCodes, function(code) {
invalidCodes.forEach(function(code) {
goog.dom.forms.setValue(e, code);
assertNull(this.component.checkAndGetPhoneConfirmationCode());
}, this);
Expand All @@ -84,7 +83,7 @@ element.PhoneConfirmationCodeTestHelper.prototype
'123456',
'000123', // leading zeroes
];
goog.array.forEach(validCodes, function(code) {
validCodes.forEach(function(code) {
goog.dom.forms.setValue(e, code);
assertNotNull(this.component.checkAndGetPhoneConfirmationCode());
}, this);
Expand Down
19 changes: 9 additions & 10 deletions javascript/ui/element/phonenumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,16 +228,15 @@ element.phoneNumber.isCountryAvailable_ = function(countryId,
* @private
*/
element.phoneNumber.createListBoxItemList_ = function(availableCountries) {
return goog.array.map(availableCountries,
function(country) {
return {
id: country.e164_key,
iconClass: 'firebaseui-flag ' +
element.phoneNumber.getFlagClass_(country),
label: country.name + ' ' +
element.phoneNumber.makeDisplayableCountryCode_(country.e164_cc)
};
});
return availableCountries.map(function(country) {
return {
id: country.e164_key,
iconClass:
'firebaseui-flag ' + element.phoneNumber.getFlagClass_(country),
label: country.name + ' ' +
element.phoneNumber.makeDisplayableCountryCode_(country.e164_cc)
};
});
};


Expand Down
2 changes: 2 additions & 0 deletions javascript/ui/element/tospptesthelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ goog.provide('firebaseui.auth.ui.element.TosPpTestHelper');
goog.setTestOnly('firebaseui.auth.ui.element.TosPpTestHelper');

goog.require('firebaseui.auth.ui.element');
/** @suppress {extraRequire} */
goog.require('firebaseui.auth.ui.element.ElementTestHelper');
goog.require('goog.testing.events');


Expand Down
4 changes: 1 addition & 3 deletions javascript/ui/mdl.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,10 @@ firebaseui.auth.ui.mdl.performOnMdlComponents_ = function(element, operation) {
!window['componentHandler'][operation]) {
return;
}
goog.array.forEach(firebaseui.auth.ui.mdl.MDL_COMPONENT_CLASSES_,
function(className) {
firebaseui.auth.ui.mdl.MDL_COMPONENT_CLASSES_.forEach(function(className) {
if (goog.dom.classlist.contains(element, className)) {
window['componentHandler'][operation](element);
}

var matchingElements = goog.dom.getElementsByClass(className, element);
goog.array.forEach(matchingElements, function(mdlElement) {
window['componentHandler'][operation](mdlElement);
Expand Down
6 changes: 1 addition & 5 deletions javascript/ui/page/selecttenant_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
goog.module('firebaseui.auth.ui.page.SelectTenantTest');
goog.setTestOnly();

const InfoBarTestHelper =
goog.require('firebaseui.auth.ui.element.InfoBarTestHelper');

const KeyCodes = goog.require('goog.events.KeyCodes');
const MockClock = goog.require('goog.testing.MockClock');
const PageTestHelper = goog.require('firebaseui.auth.ui.page.PageTestHelper');
Expand All @@ -35,8 +34,6 @@ const testSuite = goog.require('goog.testing.testSuite');
let mockClock;
let root;
let component;
const infoBarTestHelper =
new InfoBarTestHelper().registerTests();
const tosPpTestHelper = new TosPpTestHelper().registerTests();
const pageTestHelper = new PageTestHelper().registerTests();

Expand Down Expand Up @@ -78,7 +75,6 @@ testSuite({
TosPpTestHelper.prototype.onPpLinkClick,
tosPpTestHelper));
component.render(root);
infoBarTestHelper.setComponent(component);
tosPpTestHelper.setComponent(component);
// Reset previous state of tosPp helper.
tosPpTestHelper.resetState();
Expand Down
9 changes: 5 additions & 4 deletions javascript/ui/page/unauthorizeduser.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ const page = goog.require('firebaseui.auth.soy2.page');
*/
class UnauthorizedUser extends Base {
/**
* @param {?string} email The user's email.
* @param {?string} userIdentifier The user identifier of the account, can be
* email address or phone number.
* @param {function()} onCancelClick Callback to invoke when the back button
* is clicked.
* @param {?string=} adminEmail The admin email to contact with.
Expand All @@ -43,12 +44,12 @@ class UnauthorizedUser extends Base {
* @param {?DomHelper=} domHelper Optional DOM helper.
*/
constructor(
email, onCancelClick, adminEmail, emailHelperCallback, tosCallback,
privacyPolicyCallback, domHelper) {
userIdentifier, onCancelClick, adminEmail, emailHelperCallback,
tosCallback, privacyPolicyCallback, domHelper) {
super(
page.unauthorizedUser,
{
email: email,
userIdentifier: userIdentifier,
adminEmail: adminEmail,
displayHelpLink: !!emailHelperCallback
},
Expand Down
Loading

0 comments on commit 271f012

Please sign in to comment.