Only permit registering username when allowed

The gr-account-details form used in settings houses logic for when to
display an editable username. In particular, the username is only
editable when the server config lists "USER_NAME" as an "editable
account field" and a username hasn't already been set.

However, this logic was not used in the gr-registration-dialog form.
Rather, an input for username was always shown, whether or not it was
editable. As a result, if a user tried to fill out a form when the
username could not be saved, it would always result in an error.

Use the same logic as gr-account-details in gr-registration-dialog for
deciding whether the username should be editable.

Bug: Issue 8167
Change-Id: Ia614b5996f0bcfe7fdf785efcebbbde27919e7d3
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 6a2bfe0..c9e05e4 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -220,8 +220,9 @@
           view="[[params.view]]"
           on-close="_handleKeyboardShortcutDialogClose"></gr-keyboard-shortcuts-dialog>
     </gr-overlay>
-    <gr-overlay id="registration" with-backdrop>
+    <gr-overlay id="registrationOverlay" with-backdrop>
       <gr-registration-dialog
+          id="registrationDialog"
           settings-url="[[_settingsUrl]]"
           on-account-detail-update="_handleAccountDetailUpdate"
           on-close="_handleRegistrationDialogClose">
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 3b66ae8..2a247b9 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -191,7 +191,10 @@
         this.async(() => this.set('_showPluginScreen', true), 1);
       }
       if (this.params.justRegistered) {
-        this.$.registration.open();
+        this.$.registrationOverlay.open();
+        this.$.registrationDialog.loadData().then(() => {
+          this.$.registrationOverlay.refit();
+        });
       }
       this.$.header.unfloat();
     },
@@ -272,7 +275,7 @@
 
     _handleRegistrationDialogClose(e) {
       this.params.justRegistered = false;
-      this.$.registration.close();
+      this.$.registrationOverlay.close();
     },
 
     _computeShadowClass(isShadowDom) {
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
index 79c8a3b..7a8cd6f 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
@@ -32,6 +32,16 @@
       main {
         max-width: 46em;
       }
+      :host(.loading) main {
+        display: none;
+      }
+      .loadingMessage {
+        display: none;
+        font-style: italic;
+      }
+      :host(.loading) .loadingMessage {
+        display: block;
+      }
       hr {
         margin-top: 1em;
         margin-bottom: 1em;
@@ -54,9 +64,13 @@
       input {
         width: 20em;
       }
+      section.hide {
+        display: none;
+      }
     </style>
     <div class="container gr-form-styles">
       <header>Please confirm your contact information</header>
+      <div class="loadingMessage">Loading...</div>
       <main>
         <p>
           The following contact information was automatically obtained when you
@@ -73,7 +87,7 @@
               bind-value="{{_account.name}}"
               disabled="[[_saving]]">
         </section>
-        <section>
+        <section class$="[[_computeUsernameClass(_usernameMutable)]]">
           <div class="title">Username</div>
           <input
               is="iron-input"
@@ -108,7 +122,7 @@
             id="saveButton"
             primary
             link
-            disabled="[[_computeSaveDisabled(_account.name, _account.username, _account.email, _saving)]]"
+            disabled="[[_computeSaveDisabled(_account.name, _account.email, _saving)]]"
             on-tap="_handleSave">Save</gr-button>
       </footer>
     </div>
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
index 051668c..c6cd578 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
@@ -43,32 +43,56 @@
           return {email: null, name: null, username: null};
         },
       },
+      _usernameMutable: {
+        type: Boolean,
+        computed: '_computeUsernameMutable(_serverConfig, _account.username)',
+      },
+      _loading: {
+        type: Boolean,
+        value: true,
+        observer: '_loadingChanged',
+      },
       _saving: {
         type: Boolean,
         value: false,
       },
+      _serverConfig: Object,
     },
 
     hostAttributes: {
       role: 'dialog',
     },
 
-    attached() {
-      this.$.restAPI.getAccount().then(account => {
+    loadData() {
+      this._loading = true;
+
+      const loadAccount = this.$.restAPI.getAccount().then(account => {
         // Using Object.assign here allows preservation of the default values
         // supplied in the value generating function of this._account, unless
         // they are overridden by properties in the account from the response.
         this._account = Object.assign({}, this._account, account);
       });
+
+      const loadConfig = this.$.restAPI.getConfig().then(config => {
+        this._serverConfig = config;
+      });
+
+      return Promise.all([loadAccount, loadConfig]).then(() => {
+        this._loading = false;
+      });
     },
 
     _save() {
       this._saving = true;
       const promises = [
         this.$.restAPI.setAccountName(this.$.name.value),
-        this.$.restAPI.setAccountUsername(this.$.username.value),
         this.$.restAPI.setPreferredAccountEmail(this.$.email.value || ''),
       ];
+
+      if (this._usernameMutable) {
+        promises.push(this.$.restAPI.setAccountUsername(this.$.username.value));
+      }
+
       return Promise.all(promises).then(() => {
         this._saving = false;
         this.fire('account-detail-update');
@@ -90,8 +114,21 @@
       this.fire('close');
     },
 
-    _computeSaveDisabled(name, username, email, saving) {
-      return !name || !username || !email || saving;
+    _computeSaveDisabled(name, email, saving) {
+      return !name || !email || saving;
+    },
+
+    _computeUsernameMutable(config, username) {
+      return config.auth.editable_account_fields.includes('USER_NAME') &&
+          !username;
+    },
+
+    _computeUsernameClass(usernameMutable) {
+      return usernameMutable ? '' : 'hide';
+    },
+
+    _loadingChanged() {
+      this.classList.toggle('loading', this._loading);
     },
   });
 })();
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
index e4560ff..93a3188 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
@@ -45,13 +45,13 @@
     let sandbox;
     let _listeners;
 
-    setup(done => {
+    setup(() => {
       sandbox = sinon.sandbox.create();
       _listeners = {};
 
       account = {
         name: 'name',
-        username: 'username',
+        username: null,
         email: 'email',
         secondary_emails: [
           'email2',
@@ -61,8 +61,6 @@
 
       stub('gr-rest-api-interface', {
         getAccount() {
-        // Once the account is resolved, we can let the test proceed.
-          flush(done);
           return Promise.resolve(account);
         },
         setAccountName(name) {
@@ -77,9 +75,15 @@
           account.email = email;
           return Promise.resolve();
         },
+        getConfig() {
+          return Promise.resolve(
+              {auth: {editable_account_fields: ['USER_NAME']}});
+        },
       });
 
       element = fixture('basic');
+
+      return element.loadData();
     });
 
     teardown(() => {
@@ -136,7 +140,7 @@
 
         // Nothing should be committed yet.
         assert.equal(account.name, 'name');
-        assert.equal(account.username, 'username');
+        assert.isNotOk(account.username);
         assert.equal(account.email, 'email');
 
         // Save and verify new values are committed.
@@ -158,12 +162,22 @@
 
     test('save btn disabled', () => {
       const compute = element._computeSaveDisabled;
-      assert.isTrue(compute('', '', '', false));
-      assert.isTrue(compute('', 'test', 'test', false));
-      assert.isTrue(compute('test', '', 'test', false));
-      assert.isTrue(compute('test', 'test', '', false));
-      assert.isTrue(compute('test', 'test', 'test', true));
-      assert.isFalse(compute('test', 'test', 'test', false));
+      assert.isTrue(compute('', '', false));
+      assert.isTrue(compute('', 'test', false));
+      assert.isTrue(compute('test', '', false));
+      assert.isTrue(compute('test', 'test', true));
+      assert.isFalse(compute('test', 'test', false));
+    });
+
+    test('_computeUsernameMutable', () => {
+      assert.isTrue(element._computeUsernameMutable(
+          {auth: {editable_account_fields: ['USER_NAME']}}, null));
+      assert.isFalse(element._computeUsernameMutable(
+          {auth: {editable_account_fields: ['USER_NAME']}}, 'abc'));
+      assert.isFalse(element._computeUsernameMutable(
+          {auth: {editable_account_fields: []}}, null));
+      assert.isFalse(element._computeUsernameMutable(
+          {auth: {editable_account_fields: []}}, 'abc'));
     });
   });
 </script>