Commit fd3bb4db authored by Muhammad Umair Khan's avatar Muhammad Umair Khan
Browse files

Add SSL warning dialog and cancel option for sandbox sign-in

* js-apps/frontend: Create the SslTrustErrorDialog component and integrate it within app-constants and app-container to provide users with direct feedback and instructions when attempting to load the sandbox under an untrusted or bypassed self-signed SSL certificate context.
* app-container: Add an HTTPS protocol and IP address validation pattern in componentDidMount to automatically detect non-secure/untrusted raw IP connections and prompt the SSL trust warning dialog.
* sign-in-wait-dialog: Introduce a 'Cancel' close label button inside the initialization loading dialog, allowing users to abort the sign-in procedure.
parent d02fc166
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ export const DIALOG_HELP_GETTING_STARTED = 'DIALOG_HELP_GETTING_STARTED';
export const DIALOG_CONFIRM_DELETE_APP = 'DIALOG_CONFIRM_DELETE_APP';
export const DIALOG_CREATE_NEW_APP = 'DIALOG_CREATE_NEW_APP';
export const DIALOG_VERSION = 'DIALOG_VERSION';
export const DIALOG_SSL_TRUST_ERROR = 'DIALOG_SSL_TRUST_ERROR';

// Alert Types
export const ALERT_DEGRADED_NETWORK = 'ALERT_DEGRADED_NETWORK';
+1 −1
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ class SignInWaitDialog extends Component {
        open={this.props.open}
        onClose={this.props.onClose}
        onSubmit={this.props.onClose}
        submitLabel={''}
        closeLabel={'Cancel'}
      >
        <p>This may take a few seconds...</p>
        <p>You will automatically be redirected to the sandbox page when ready.</p>
+50 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2026 ETSI.  All rights reserved.
 */

import React, { Component } from 'react';
import BasicDialog from './basic-dialog';

class SslTrustErrorDialog extends Component {
  render() {
    return (
      <BasicDialog
        title={this.props.title}
        open={this.props.open}
        onClose={this.props.onClose}
        onSubmit={this.props.onClose}
        submitLabel={'Ok'}
      >
        <div tabIndex="0" style={{ outline: 'none' }}>
          <button
            style={{ position: 'absolute', width: '1px', height: '1px', padding: '0', margin: '-1px', overflow: 'hidden', clip: 'rect(0, 0, 0, 0)', border: '0' }}
            data-mdc-dialog-initial-focus
            onClick={this.props.onClose}
          >
            Close
          </button>
          <div style={{ color: '#d32f2f', fontWeight: 'bold', marginBottom: '15px', fontSize: '1.1rem' }}>
            Cookie Storage Blocked (Untrusted HTTPS)
          </div>
          <p style={{ marginBottom: '10px' }}>Your browser blocked the MEC Sandbox session cookies because the server's SSL certificate is not trusted.</p>
          <p style={{ marginBottom: '10px' }}>This typically happens when accessing the sandbox via a raw IP address or a self-signed HTTPS domain name.</p>
          <p style={{ marginBottom: '15px', color: '#e65100', backgroundColor: '#fff3e0', padding: '10px', borderLeft: '4px solid #ff9800', borderRadius: '2px' }}>
            <strong>Why this is required:</strong> The MEC Sandbox session relies on secure cookies. When logging in via GitHub, the browser redirects back to the sandbox. If the SSL certificate is untrusted, the browser blocks these session cookies. This prevents the sandbox from initializing correctly, causes silent sign-outs, and prevents user pods from being successfully deleted upon logout.
          </p>
          <div style={{ marginTop: '20px', padding: '15px', backgroundColor: '#f9f9f9', border: '1px solid #e0e0e0', borderRadius: '4px' }}>
            <strong style={{ display: 'block', marginBottom: '8px' }}>How to fix this in your browser:</strong>
            <ol style={{ paddingLeft: '20px', margin: 0, lineHeight: '1.5' }}>
              <li style={{ marginBottom: '8px' }}>Click the <strong>"Not secure" / "Dangerous"</strong> lock/warning icon in your browser's address bar.</li>
              <li style={{ marginBottom: '8px' }}>Go to certificate details (often under "Connection is not secure" &rarr; "Certificate is not valid").</li>
              <li style={{ marginBottom: '8px' }}>Export/save the certificate to a local file.</li>
              <li style={{ marginBottom: '8px' }}>Import the exported certificate file into your Operating System's <strong>"Trusted Root Certification Authorities"</strong> store.</li>
              <li>Restart your browser completely and sign in again.</li>
            </ol>
          </div>
        </div>
      </BasicDialog>
    );
  }
}

export default SslTrustErrorDialog;
+48 −19
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import HelpGettingStartedDialog from '../components/dialogs/help-getting-started
import ConfirmDeleteAppDialog from '../components/dialogs/confirm-delete-app-dialog';
import CreateNewAppDialog from '../components/dialogs/create-new-app-dialog';
import VersionDialog from '../components/dialogs/version-dialog';
import SslTrustErrorDialog from '../components/dialogs/ssl-trust-error-dialog';

import * as meepAuthSvcRestApiClient from '../../../../../js-packages/meep-auth-svc-client/src/index.js';
import * as meepPlatformCtrlRestApiClient from '../../../../../js-packages/meep-platform-ctrl-client/src/index.js';
@@ -46,6 +47,7 @@ import {
  DIALOG_CONFIRM_DELETE_APP,
  DIALOG_CREATE_NEW_APP,
  DIALOG_VERSION,
  DIALOG_SSL_TRUST_ERROR,
  ALERT_DEGRADED_NETWORK,
  STATUS_SIGNED_IN,
  STATUS_SIGNING_IN,
@@ -244,10 +246,16 @@ class AppContainer extends Component {
  }

  componentDidMount() {
    document.title = 'ETSI MEC Sandbox';
    document.title = 'EdgeNative - Connector';
    this.setBasePaths();
    this.startTimers();
    this.monitorTabFocus();

    // Check if accessing via HTTPS on a raw IP address (untrusted SSL context)
    const isIpAddress = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(window.location.hostname);
    if (window.location.protocol === 'https:' && isIpAddress) {
      this.props.changeCurrentDialog(DIALOG_SSL_TRUST_ERROR);
    }
  }

  componentDidUpdate(prevProps) {
@@ -444,7 +452,7 @@ class AppContainer extends Component {
        errMsg = 'Invalid credentials. Please try again...';
        break;
      case StatusCodes.SERVICE_UNAVAILABLE:
        errMsg = 'All MEC Sandboxes are currently in use. Please try again later...';
        errMsg = 'All EdgeNative - Connector sandboxes are currently in use. Please try again later...';
        break;
      default:
        errMsg = 'Login failure. Please try again...';
@@ -542,7 +550,7 @@ class AppContainer extends Component {
        errMsg = 'Invalid credentials. Please try again...';
        break;
      case StatusCodes.SERVICE_UNAVAILABLE:
        errMsg = 'All MEC Sandboxes are currently in use. Please try again later...';
        errMsg = 'All EdgeNative - Connector sandboxes are currently in use. Please try again later...';
        break;
      default:
        errMsg = 'Login failure. Please try again...';
@@ -628,11 +636,15 @@ class AppContainer extends Component {
   */
  getSandboxStateCb(error, data) {
    if (error) {
      // Silently ignore 503 errors during startup - services are still initializing
      if (error.status === StatusCodes.SERVICE_UNAVAILABLE) {
        return;
      }
      if (error.status === StatusCodes.UNAUTHORIZED) {
        this.stopSandboxCreationTimer();
        this.props.changeCurrentDialog(null);
        this.props.changeSignInStatus(STATUS_SIGNED_OUT);
      }
      this.props.changeCurrentDialog(null);
      return;
    }
    if (data) {
@@ -777,6 +789,16 @@ class AppContainer extends Component {
      return;
    }

    // Check if response indicates no scenario is active (new format)
    if (data && data.status === 'inactive') {
      this.updateScenario(null);
      this.props.changeApiTable(null);
      this.props.changeApiDetailedData(null);
      this.props.changeAppInstanceTable([]);
      this.props.changeSvcTable([]);
      return;
    }

    // Validate scenario
    if (data && !data.deployment) {
      return;
@@ -1347,12 +1369,19 @@ class AppContainer extends Component {
          mepNames={this.props.mepList}
        />
        <VersionDialog
          title='MEC Sandbox Versions'
          title='EdgeNative - Connector Versions'
          open={this.props.currentDialog === DIALOG_VERSION}
          onClose={() => {
            this.closeDialog();
          }}
        />
        <SslTrustErrorDialog
          title='Connection SSL/Cookie Security Alert'
          open={this.props.currentDialog === DIALOG_SSL_TRUST_ERROR}
          onClose={() => {
            this.closeDialog();
          }}
        />
      </>
    );
  }
@@ -1382,10 +1411,10 @@ class AppContainer extends Component {
    } else {
      return (
        <div style={{ margin: 20 }}>
          <h1>ETSI MEC Sandbox</h1>
          <h3>Unfortunately, MEC Sandbox does not look great with a resolution of {width} x {height}</h3>
          <h1>EdgeNative - Connector</h1>
          <h3>Unfortunately, EdgeNative - Connector does not look great with a resolution of {width} x {height}</h3>
          <p>
            Please zoom out and refresh your browser or load the MEC Sandbox from a browser with a
            Please zoom out and refresh your browser or load EdgeNative - Connector from a browser with a
            minimum width of <b>{MIN_SCREEN_WIDTH}</b> pixels.
          </p>
          <p>NOTE: keyboard zoom shortcut is <i>ctrl+- (minus)</i></p>