chromium/chrome/test/data/webui/cr_components/chromeos/network/network_config_wifi_test.js

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'chrome://os-settings/strings.m.js';
import 'chrome://resources/ash/common/network/network_config.js';

import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
import {CrosNetworkConfigRemote, HiddenSsidMode, SecurityType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
import {NetworkType, OncSource, PolicySource} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
import {FakeNetworkConfig} from 'chrome://webui-test/chromeos/fake_network_config_mojom.js';

suite('network-config-wifi', function() {
  let networkConfig;

  /** @type {?CrosNetworkConfigRemote} */
  let mojoApi_ = null;

  const kCaHash = 'CAHASH';
  const kCaPem = 'test-pem';
  const kUserHash1 = 'USERHASH1';
  const kUserHash2 = 'USERHASH2';
  const kUserCertId = 'test-cert-id';
  const kMissedEapDataErr = 'missingEapDefaultServerCaSubjectVerification';
  const kServerCaCertsNotProvided = false;
  const kAddServerCaCerts = true;

  suiteSetup(function() {
    mojoApi_ = new FakeNetworkConfig();
    MojoInterfaceProviderImpl.getInstance().remote_ = mojoApi_;
  });

  function setNetworkConfig(properties) {
    assertTrue(!!properties.guid);
    mojoApi_.setManagedPropertiesForTest(properties);
    PolymerTest.clearBody();
    networkConfig = document.createElement('network-config');
    networkConfig.guid = properties.guid;
    networkConfig.managedProperties = properties;
  }

  function setNetworkType(type, security) {
    PolymerTest.clearBody();
    networkConfig = document.createElement('network-config');
    networkConfig.type = OncMojo.getNetworkTypeString(type);
    if (security !== undefined) {
      networkConfig.securityType_ = security;
    }
  }

  function initNetworkConfig() {
    document.body.appendChild(networkConfig);
    networkConfig.init();
    flush();
  }

  function initNetworkConfigWithCerts(hasServerCa, hasUserCert) {
    const serverCas = [];
    const userCerts = [];
    if (hasServerCa) {
      serverCas.push({
        hash: kCaHash,
        pemOrId: kCaPem,
        availableForNetworkAuth: true,
        hardwareBacked: true,
        deviceWide: true,
      });
    }
    if (hasUserCert) {
      userCerts.push({
        hash: kUserHash1,
        pemOrId: kUserCertId,
        availableForNetworkAuth: true,
        hardwareBacked: true,
        deviceWide: false,
      });
    }
    mojoApi_.setCertificatesForTest(serverCas, userCerts);
    initNetworkConfig();
  }

  function flushAsync() {
    flush();
    return new Promise(resolve => {
      networkConfig.async(resolve);
    });
  }

  /**
   * Simulate an element of id |elementId| fires enter event.
   * @param {string} elementId
   */
  function simulateEnterPressedInElement(elementId) {
    const element = networkConfig.$$(`#${elementId}`);
    networkConfig.connectOnEnter = true;
    assertTrue(!!element);
    element.fire('enter', {path: [element]});
  }

  suite('New WiFi Config', function() {
    setup(function() {
      mojoApi_.resetForTest();
      setNetworkType(NetworkType.kWiFi);
      initNetworkConfig();
    });

    teardown(function() {
      PolymerTest.clearBody();
    });

    test('Default', function() {
      assertTrue(!!networkConfig.$$('#share'));
      assertTrue(!!networkConfig.$$('#ssid'));
      assertTrue(!!networkConfig.$$('#security'));
      assertFalse(networkConfig.$$('#security').disabled);
    });

    test('Passphrase field shows', function() {
      assertFalse(!!networkConfig.$$('#wifi-passphrase'));
      networkConfig.$$('#security').value = SecurityType.kWpaPsk;
      return flushAsync().then(() => {
        assertTrue(!!networkConfig.$$('#wifi-passphrase'));
      });
    });

    test('New networks are explicitly not hidden', async () => {
      networkConfig.save();

      await flushAsync();

      const props = mojoApi_.getPropertiesToSetForTest();
      assertEquals(props.typeConfig.wifi.hiddenSsid, HiddenSsidMode.kDisabled);
    });
  });

  suite('Existing WiFi Config', function() {
    setup(function() {
      mojoApi_.resetForTest();
      const wifi1 = OncMojo.getDefaultManagedProperties(
          NetworkType.kWiFi, 'someguid', '');
      wifi1.name = OncMojo.createManagedString('somename');
      wifi1.source = OncSource.kDevice;
      wifi1.typeProperties.wifi.security = SecurityType.kWepPsk;
      wifi1.typeProperties.wifi.ssid.activeValue = '11111111111';
      wifi1.typeProperties.wifi.passphrase = {activeValue: 'test_passphrase'};
      setNetworkConfig(wifi1);
      initNetworkConfig();
    });

    teardown(function() {
      PolymerTest.clearBody();
    });

    test('Default', function() {
      return flushAsync().then(() => {
        assertEquals('someguid', networkConfig.managedProperties.guid);
        assertEquals(
            'somename', networkConfig.managedProperties.name.activeValue);
        assertFalse(!!networkConfig.$$('#share'));
        assertTrue(!!networkConfig.$$('#ssid'));
        assertTrue(!!networkConfig.$$('#security'));
        assertTrue(networkConfig.$$('#security').disabled);
      });
    });

    test('WiFi input fires enter event on keydown', function() {
      return flushAsync().then(() => {
        assertFalse(networkConfig.propertiesSent_);
        simulateEnterPressedInElement('ssid');
        assertTrue(networkConfig.propertiesSent_);
      });
    });

    test('Remove error text when input key is pressed', function() {
      return flushAsync().then(() => {
        networkConfig.error = 'bad-passphrase';
        const passwordInput = networkConfig.$$('#wifi-passphrase');
        assertTrue(!!passwordInput);
        assertTrue(!!networkConfig.error);

        passwordInput.fire('keypress');
        flush();
        assertFalse(!!networkConfig.error);
      });
    });

    test('Networks\' hidden SSID mode is not overwritten', async () => {
      await flushAsync();

      networkConfig.save();

      await flushAsync();

      const props = mojoApi_.getPropertiesToSetForTest();
      assertEquals(props.typeConfig.wifi.hiddenSsid, HiddenSsidMode.kAutomatic);
    });
  });

  suite('Non-VPN EAP', function() {
    setup(function() {
      mojoApi_.resetForTest();
    });

    teardown(function() {
      PolymerTest.clearBody();
    });

    function setAuthenticated() {
      // Logged in users can share new networks.
      networkConfig.shareAllowEnable = true;
      // Authenticated networks default to not shared.
      networkConfig.shareDefault = false;
    }

    async function saveNetworkConfig() {
      networkConfig.save();
      await flushAsync();
    }

    async function setSerializedSubjectAltNameMatch(value) {
      networkConfig.serializedSubjectAltNameMatch_ = value;
      await saveNetworkConfig();
    }

    async function setSerializedDomainSuffixMatch(value) {
      networkConfig.serializedDomainSuffixMatch_ = value;
      await saveNetworkConfig();
    }

    function isDefaultServerCaSelected() {
      return 'default' === networkConfig.selectedServerCaHash_;
    }

    function isConfigErrorsPresent() {
      return '' !== networkConfig.error;
    }

    function isMissedEapDataErrorShown() {
      return kMissedEapDataErr === networkConfig.error;
    }

    function getErrorMessage(eapType) {
      return 'Failed test for eapType = ' + eapType;
    }

    async function initiateWiFiEapConfig(isAddServerCA, eapType) {
      setNetworkType(NetworkType.kWiFi, SecurityType.kWpaEap);
      setAuthenticated();
      initNetworkConfigWithCerts(
          /* hasServerCa= */ isAddServerCA, /* hasUserCert= */ true);
      networkConfig.shareNetwork_ = true;
      networkConfig.set('eapProperties_.outer', eapType);
      await mojoApi_.whenCalled('getNetworkCertificates');
      networkConfig.save();
      await flushAsync();
    }

    test('WiFi EAP Default Outer', async function() {
      setNetworkType(NetworkType.kWiFi, SecurityType.kWpaEap);
      setAuthenticated();
      initNetworkConfig();
      networkConfig.shareNetwork_ = false;
      await mojoApi_.whenCalled('getNetworkCertificates');
      await flushAsync();
      const outer = networkConfig.$$('#outer');
      // 'PEAP' should be the default 'Outer' protocol.
      assertEquals('PEAP', outer.value);
    });

    test('WiFi EAP-TLS No Certs', function() {
      setNetworkType(NetworkType.kWiFi, SecurityType.kWpaEap);
      setAuthenticated();
      initNetworkConfig();
      networkConfig.shareNetwork_ = false;
      networkConfig.set('eapProperties_.outer', 'EAP-TLS');
      return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
        return flushAsync().then(() => {
          const outer = networkConfig.$$('#outer');
          assertEquals('EAP-TLS', outer.value);
          // Check that with no certificates, 'default' and 'no-certs' are
          // selected.
          assertEquals('default', networkConfig.selectedServerCaHash_);
          assertEquals('no-certs', networkConfig.selectedUserCertHash_);
        });
      });
    });

    test('WiFi EAP-TLS Certs', function() {
      setNetworkType(NetworkType.kWiFi, SecurityType.kWpaEap);
      setAuthenticated();
      mojoApi_.setCertificatesForTest(
          [{
            hash: kCaHash,
            availableForNetworkAuth: true,
            hardwareBacked: true,
            deviceWide: true,
          }],
          [{
            hash: kUserHash1,
            pemOrId: kUserCertId,
            availableForNetworkAuth: true,
            hardwareBacked: true,
            deviceWide: false,
          }]);
      initNetworkConfig();
      networkConfig.shareNetwork_ = false;
      networkConfig.set('eapProperties_.outer', 'EAP-TLS');
      return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
        return flushAsync().then(() => {
          // The first Server CA  and User certificate should be selected.
          assertEquals(kCaHash, networkConfig.selectedServerCaHash_);
          assertEquals(kUserHash1, networkConfig.selectedUserCertHash_);
        });
      });
    });

    test('WiFi EAP-TLS Certs Shared', function() {
      setNetworkType(NetworkType.kWiFi, SecurityType.kWpaEap);
      setAuthenticated();
      mojoApi_.setCertificatesForTest(
          [{
            hash: kCaHash,
            availableForNetworkAuth: true,
            hardwareBacked: true,
            deviceWide: true,
          }],
          [
            {
              hash: kUserHash1,
              pemOrId: kUserCertId,
              availableForNetworkAuth: true,
              hardwareBacked: true,
              deviceWide: false,
            },
            {
              hash: kUserHash2,
              pemOrId: kUserCertId,
              availableForNetworkAuth: true,
              hardwareBacked: true,
              deviceWide: true,
            },
          ]);
      initNetworkConfig();
      networkConfig.shareNetwork_ = true;
      networkConfig.set('eapProperties_.outer', 'EAP-TLS');
      return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
        return flushAsync().then(() => {
          // The first Server CA should be selected.
          assertEquals(kCaHash, networkConfig.selectedServerCaHash_);
          // Second User Hash should be selected since it is a device cert.
          assertEquals(kUserHash2, networkConfig.selectedUserCertHash_);
        });
      });
    });

    test('WiFi PEAP No Certs', async function() {
      setNetworkType(NetworkType.kWiFi, SecurityType.kWpaEap);
      setAuthenticated();
      initNetworkConfig();
      networkConfig.shareNetwork_ = false;
      networkConfig.set('eapProperties_.outer', 'PEAP');
      await mojoApi_.whenCalled('getNetworkCertificates');
      await flushAsync();
      const outer = networkConfig.$$('#outer');
      assertEquals('PEAP', outer.value);
      // 'default' Server CA should be selected in case of no certificates
      assertEquals('default', networkConfig.selectedServerCaHash_);
      // 'no-user-cert' is selected as user certificate because a
      // user certificate is not needed for PEAP
      assertEquals('no-user-cert', networkConfig.selectedUserCertHash_);
    });

    test('WiFi PEAP Certs', async function() {
      setNetworkType(NetworkType.kWiFi, SecurityType.kWpaEap);
      setAuthenticated();
      mojoApi_.setCertificatesForTest(
          [{
            hash: kCaHash,
            availableForNetworkAuth: true,
            hardwareBacked: true,
            deviceWide: true,
          }],
          [{
            hash: kUserHash1,
            pemOrId: kUserCertId,
            availableForNetworkAuth: true,
            hardwareBacked: true,
            deviceWide: false,
          }]);
      initNetworkConfig();
      networkConfig.shareNetwork_ = false;
      networkConfig.set('eapProperties_.outer', 'PEAP');
      await mojoApi_.whenCalled('getNetworkCertificates');
      await flushAsync();
      const outer = networkConfig.$$('#outer');
      assertEquals('PEAP', outer.value);
      // The first Server CA should be selected.
      assertEquals(kCaHash, networkConfig.selectedServerCaHash_);
      // 'no-user-cert' is selected as user certificate because a
      // user certificate is not needed for PEAP
      assertEquals('no-user-cert', networkConfig.selectedUserCertHash_);
    });

    // Testing different WiFi EAP types with default Server CA certs.
    // Expected to see errors if both
    // [SerializedDomainSuffixMatch,  SerializedSubjectAltNameMatch] are empty.
    ['EAP-TLS', 'EAP-TTLS', 'PEAP'].forEach(eapType => {
      test('WiFi EAP Default CA Cert', async function() {
        const errorMessage = getErrorMessage(eapType);
        // No Server CA certificates provided, the default one will be selected.
        await initiateWiFiEapConfig(kServerCaCertsNotProvided, eapType);

        assertTrue(isDefaultServerCaSelected(), errorMessage);
        // Expected to see error because required fields are empty.
        assertTrue(isConfigErrorsPresent(), errorMessage);

        // Set an empty DomainSuffixMatch doesn't clear the error.
        await setSerializedDomainSuffixMatch('');
        assertTrue(isConfigErrorsPresent(), errorMessage);
        // Set an empty SerializedSubjectAltNameMatch doesn't clear
        // the error.
        await setSerializedSubjectAltNameMatch('');
        assertTrue(isMissedEapDataErrorShown(), errorMessage);

        // Set a non empty DomainSuffixMatch clears the error.
        await setSerializedDomainSuffixMatch('test.com');
        assertFalse(isConfigErrorsPresent(), errorMessage);
      });
    });

    // Testing that managed WiFi EAP networks which use the default Server CA
    // cert are not required to set a SerializedDomainSuffixMatch or
    // SerializedSubjectAltNameMatch.
    ['EAP-TLS', 'EAP-TTLS', 'PEAP'].forEach(eapType => {
      test('WiFi EAP Default CA Cert Managed EAP Settings', async function() {
        const errorMessage = getErrorMessage(eapType);
        mojoApi_.resetForTest();
        const wifi1 = OncMojo.getDefaultManagedProperties(
            NetworkType.kWiFi, 'testguid', '');
        const managed_eap = {
          outer: {activeValue: eapType},
          useSystemCas: {
            activeValue: true,
            policySource: PolicySource.kUserPolicyEnforced,
          },
        };
        wifi1.typeProperties.wifi.security = SecurityType.kWpaEap;
        wifi1.typeProperties.wifi.eap = managed_eap;

        setNetworkConfig(wifi1);
        initNetworkConfigWithCerts(
            /* hasServerCa= */ false, /* hasUserCert= */ true);
        await mojoApi_.whenCalled('getNetworkCertificates');
        await flushAsync();
        assertTrue(isDefaultServerCaSelected());
        assertFalse(isConfigErrorsPresent());
      });
    });

    // Testing LEAP with default Server CA certs. Expected to see
    // no errors because LEAP is not a certificate-based authentication
    // protocol.
    test('WiFi EAP Default CA Cert LEAP', async function() {
      const eapType = 'LEAP';

      // No Server CA certificates, the default one will be selected in UI.
      await initiateWiFiEapConfig(kServerCaCertsNotProvided, eapType);

      // 'default' Server CA should be selected.
      assertTrue(isDefaultServerCaSelected());
      assertFalse(isConfigErrorsPresent());

      // Setting an empty DomainSuffixMatch doesn't trigger errors.
      await setSerializedDomainSuffixMatch('');
      assertFalse(isConfigErrorsPresent());

      // Setting an empty SerializedSubjectAltNameMatch doesn't trigger errors.
      await setSerializedSubjectAltNameMatch('');
      assertFalse(isConfigErrorsPresent());

      // Setting non empty DomainSuffixMatch doesn't trigger errors.
      await setSerializedDomainSuffixMatch('test.com');
      assertFalse(isConfigErrorsPresent());

      // Setting non empty SerializedSubjectAltNameMatch doesn't trigger errors.
      await setSerializedDomainSuffixMatch('test.com');
      assertFalse(isConfigErrorsPresent());
    });

    // Testing different EAP auth methods with a non-default Server CA cert.
    // Expected to see no errors because empty [SerializedDomainSuffixMatch,
    // SerializedSubjectAltNameMatch] are allowed if a non-default (i.e.
    // non-public) Server CA is selected.
    ['EAP-TLS', 'EAP-TTLS', 'PEAP', 'LEAP'].forEach(eapType => {
      [true, false].forEach(isFeatureActive => {
        test('WiFi EAP Not Default CA Cert Flag', async function() {
          const errorMessage = getErrorMessage(eapType);

          // Adding Server CA certs, the first one will be selected by default.
          await initiateWiFiEapConfig(kAddServerCaCerts, eapType);

          // 1st Server CA should be selected and no errors.
          assertEquals(
              kCaHash, networkConfig.selectedServerCaHash_, errorMessage);
          assertFalse(isDefaultServerCaSelected(), errorMessage);
          assertFalse(isConfigErrorsPresent(), errorMessage);

          // Set an empty DomainSuffixMatch doesn't trigger errors.
          await setSerializedDomainSuffixMatch('');
          assertFalse(isConfigErrorsPresent(), errorMessage);

          // Set an empty SerializedSubjectAltNameMatch doesn't trigger errors.
          await setSerializedSubjectAltNameMatch('');
          assertFalse(isConfigErrorsPresent(), errorMessage);

          // Set non empty DomainSuffixMatch doesn't trigger errors.
          await setSerializedDomainSuffixMatch('test.com');
          assertFalse(isConfigErrorsPresent(), errorMessage);

          // Set non empty SerializedSubjectAltNameMatch doesn't trigger errors.
          await setSerializedDomainSuffixMatch('test.com');
          assertFalse(isConfigErrorsPresent(), errorMessage);
        });
      });
    });
  });
});