cloudfoundry/stratos

View on GitHub
src/frontend/packages/kubernetes/src/kubernetes/kube-config-registration/kube-config-auth.helper.ts

Summary

Maintainability
B
6 hrs
Test Coverage
import { ComponentFactoryResolver, Injector } from '@angular/core';
import { FormBuilder } from '@angular/forms';

import { ConnectEndpointData } from '../../../../core/src/features/endpoints/connect.service';
import { RowState } from '../../../../core/src/shared/components/list/data-sources-controllers/list-data-source-types';
import { EndpointAuthTypeConfig, IAuthForm } from '../../../../store/src/extension-types';
import { entityCatalog } from '../../../../store/src/public-api';
import { KUBERNETES_ENDPOINT_TYPE } from '../kubernetes-entity-factory';
import { KubeConfigFileCluster, KubeConfigFileUser } from './kube-config.types';

/**
 * Auth helper tries to figure out the Kubernetes sub-type and auth to use
 * based on the kube config file contents
 */
export class KubeConfigAuthHelper {

  authTypes: { [name: string]: EndpointAuthTypeConfig, } = {};

  public subTypes = [];

  constructor() {
    const epTypeInfo = entityCatalog.getAllEndpointTypes(false);
    const k8s = epTypeInfo.find(entity => entity.type === KUBERNETES_ENDPOINT_TYPE);
    if (k8s && k8s.definition) {
      const defn = k8s.definition;

      // Collect all of the auth types
      defn.authTypes.forEach(at => {
        this.authTypes[at.value] = at;
      });

      this.subTypes.push({ id: '', name: 'Generic' });

      // Collect all of the auth types for the sub-types
      defn.subTypes.forEach(st => {
        if (st.type !== 'config') {
          this.subTypes.push({ id: st.type, name: st.labelShort });
        }
        st.authTypes.forEach(at => {
          this.authTypes[at.value] = at;
        });
      });

      // Sort the subtypes
      this.subTypes = this.subTypes.sort((a, b) => a.name.localeCompare(b.name));
    }
  }

  // Try and parse the authentication metadata
  public parseAuth(cluster: KubeConfigFileCluster, user: KubeConfigFileUser): RowState {

    // Default subtype is generic Kubernetes ('') or previously determined/selected sub type
    cluster._subType = cluster._subType || '';

    // Certificate authentication first

    // In-file certificate authentication
    if (user.user['client-certificate-data'] && user.user['client-key-data']) {
      // We are good to go - create the form data

      // Default is generic kubernetes
      let subType = '';
      const authType = 'kube-cert-auth';
      if (cluster.cluster.server.indexOf('azmk8s.io') >= 0) {
        // Probably Azure
        subType = 'aks';
        cluster._subType = 'aks';
      }

      const authData = {
        authType,
        subType,
        values: {
          cert: user.user['client-certificate-data'],
          certKey: user.user['client-key-data']
        }
      };
      user._authData = authData;
      return {};
    }

    if (user.user['client-certificate'] || user.user['client-key']) {
      cluster._additionalUserInfo = true;
      return {
        message: 'This endpoint will be registered but not connected (additional information is required)',
        info: true
      };
    }

    const authProvider = user.user['auth-provider'];
    if (authProvider && authProvider.config) {
      if (authProvider.config['cmd-path'] && authProvider.config['cmd-path'].indexOf('gcloud') !== -1) {
        // GKE
        cluster._subType = 'gke';
        // Can not connect to GKE - user must do so manually
        cluster._additionalUserInfo = true;
        return {
          message: 'This endpoint will be registered but not connected (additional information is required)',
          info: true
        };
      }
    }

    if (
      cluster.cluster.server.indexOf('eks.amazonaws.com') >= 0 ||
      (user.user.exec && user.user.exec.command && user.user.exec.command === 'aws-iam-authenticator')
    ) {
      // Probably EKS
      cluster._subType = 'eks';
      cluster._additionalUserInfo = true;
      return {
        message: 'This endpoint will be registered but not connected (additional information is required)',
        info: true
      };
    }

    // Username and password auth
    if (user.user.username && user.user.password) {
      const authData = {
        authType: 'creds',
        subType: '',
        values: {
          username: user.user.username,
          password: user.user.password
        }
      };
      user._authData = authData;
      return {};
    }

    return { message: 'Authentication mechanism is not supported', warning: true };
  }

  // Use the auto component to get the data in the correct format for connecting to the endpoint
  public getAuthDataForConnect(resolver: ComponentFactoryResolver, injector: Injector, fb: FormBuilder, user: KubeConfigFileUser)
    : ConnectEndpointData | null {

    let data = null;

    // Get the component to us
    if (user && user._authData) {
      const authType = this.authTypes[user._authData.authType];

      const factory = resolver.resolveComponentFactory<IAuthForm>(authType.component);

      const ref = factory.create(injector);

      const form = fb.group({
        authType: authType.value,
        systemShared: false,
        authValues: fb.group(user._authData.values)
      });

      ref.instance.formGroup = form;

      // Allow the auth form to supply body content if it needs to
      const endpointFormInstance = ref.instance as any;
      if (endpointFormInstance.getBody && endpointFormInstance.getValues) {
        data = {
          authType: authType.value,
          authVal: endpointFormInstance.getValues(user._authData.values),
          systemShared: false,
          bodyContent: endpointFormInstance.getBody()
        };
      } else {
        // Use values as is
        data = {
          authType: authType.value,
          authVal: user._authData.values,
          systemShared: false,
          bodyContent: null
        };
      }

      ref.destroy();
    }
    return data;
  }
}