github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/views/reports/containers/certificates/index.tsx (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  import _ from "lodash";
    12  import React, { Fragment } from "react";
    13  import { Helmet } from "react-helmet";
    14  import { connect } from "react-redux";
    15  import { RouteComponentProps, withRouter } from "react-router-dom";
    16  
    17  import * as protos from "src/js/protos";
    18  import { certificatesRequestKey, refreshCertificates } from "src/redux/apiReducers";
    19  import { AdminUIState } from "src/redux/state";
    20  import { nodeIDAttr } from "src/util/constants";
    21  import { LongToMoment } from "src/util/convert";
    22  import Loading from "src/views/shared/components/loading";
    23  import { getMatchParamByName } from "src/util/query";
    24  
    25  interface CertificatesOwnProps {
    26    certificates: protos.cockroach.server.serverpb.CertificatesResponse;
    27    lastError: Error;
    28    refreshCertificates: typeof refreshCertificates;
    29  }
    30  
    31  const dateFormat = "Y-MM-DD HH:mm:ss";
    32  
    33  type CertificatesProps = CertificatesOwnProps & RouteComponentProps;
    34  
    35  const emptyRow = (
    36    <tr className="certs-table__row">
    37      <th className="certs-table__cell certs-table__cell--header" />
    38      <td className="certs-table__cell" />
    39    </tr>
    40  );
    41  
    42  function certificatesRequestFromProps(props: CertificatesProps) {
    43    return new protos.cockroach.server.serverpb.CertificatesRequest({
    44      node_id: getMatchParamByName(props.match, nodeIDAttr),
    45    });
    46  }
    47  
    48  /**
    49   * Renders the Certificate Report page.
    50   */
    51  export class Certificates extends React.Component<CertificatesProps, {}> {
    52    refresh(props = this.props) {
    53      props.refreshCertificates(certificatesRequestFromProps(props));
    54    }
    55  
    56    componentDidMount() {
    57      // Refresh nodes status query when mounting.
    58      this.refresh();
    59    }
    60  
    61    componentDidUpdate(prevProps: CertificatesProps) {
    62      if (!_.isEqual(this.props.location, prevProps.location)) {
    63        this.refresh(this.props);
    64      }
    65    }
    66  
    67    renderSimpleRow(header: string, value: string, title: string = "") {
    68      let realTitle = title;
    69      if (_.isEmpty(realTitle)) {
    70        realTitle = value;
    71      }
    72      return (
    73        <tr className="certs-table__row">
    74          <th className="certs-table__cell certs-table__cell--header">{header}</th>
    75          <td className="certs-table__cell" title={realTitle}>{value}</td>
    76        </tr>
    77      );
    78    }
    79  
    80    renderMultilineRow(header: string, values: string[]) {
    81      return (
    82        <tr className="certs-table__row">
    83          <th className="certs-table__cell certs-table__cell--header">{header}</th>
    84          <td className="certs-table__cell" title={_.join(values, "\n")}>
    85            <ul className="certs-entries-list">
    86              {
    87                _.chain(values)
    88                  .sort()
    89                  .map((value, key) => (
    90                    <li key={key}>
    91                      {value}
    92                    </li>
    93                  ))
    94                  .value()
    95              }
    96            </ul>
    97          </td>
    98        </tr>
    99      );
   100    }
   101  
   102    renderTimestampRow(header: string, value: Long) {
   103      const timestamp = LongToMoment(value).format(dateFormat);
   104      const title = value + "\n" + timestamp;
   105      return this.renderSimpleRow(header, timestamp, title);
   106    }
   107  
   108    renderFields(fields: protos.cockroach.server.serverpb.CertificateDetails.IFields, id: number) {
   109      return [
   110        this.renderSimpleRow("Cert ID", id.toString()),
   111        this.renderSimpleRow("Issuer", fields.issuer),
   112        this.renderSimpleRow("Subject", fields.subject),
   113        this.renderTimestampRow("Valid From", fields.valid_from),
   114        this.renderTimestampRow("Valid Until", fields.valid_until),
   115        this.renderMultilineRow("Addresses", fields.addresses),
   116        this.renderSimpleRow("Signature Algorithm", fields.signature_algorithm),
   117        this.renderSimpleRow("Public Key", fields.public_key),
   118        this.renderMultilineRow("Key Usage", fields.key_usage),
   119        this.renderMultilineRow("Extended Key Usage", fields.extended_key_usage),
   120      ];
   121    }
   122  
   123    renderCert(cert: protos.cockroach.server.serverpb.ICertificateDetails, key: number) {
   124      let certType: string;
   125      switch (cert.type) {
   126        case protos.cockroach.server.serverpb.CertificateDetails.CertificateType.CA:
   127          certType = "Certificate Authority";
   128          break;
   129        case protos.cockroach.server.serverpb.CertificateDetails.CertificateType.NODE:
   130          certType = "Node Certificate";
   131          break;
   132        case protos.cockroach.server.serverpb.CertificateDetails.CertificateType.CLIENT_CA:
   133          certType = "Client Certificate Authority";
   134          break;
   135        case protos.cockroach.server.serverpb.CertificateDetails.CertificateType.CLIENT:
   136          certType = "Client Certificate";
   137          break;
   138        case protos.cockroach.server.serverpb.CertificateDetails.CertificateType.UI_CA:
   139          certType = "UI Certificate Authority";
   140          break;
   141        case protos.cockroach.server.serverpb.CertificateDetails.CertificateType.UI:
   142          certType = "UI Certificate";
   143          break;
   144        default:
   145          certType = "Unknown";
   146      }
   147      return (
   148        <table key={key} className="certs-table">
   149          <tbody>
   150            {this.renderSimpleRow("Type", certType)}
   151            {
   152              _.map(cert.fields, (fields, id) => {
   153                const result = this.renderFields(fields, id);
   154                if (id > 0) {
   155                  result.unshift(emptyRow);
   156                }
   157                return result;
   158              })
   159            }
   160          </tbody>
   161        </table>
   162      );
   163    }
   164  
   165    renderContent = () => {
   166      const { certificates, match } = this.props;
   167      const nodeId = getMatchParamByName(match, nodeIDAttr);
   168  
   169      if (_.isEmpty(certificates.certificates)) {
   170        return <h2 className="base-heading">No certificates were found on node {nodeId}.</h2>;
   171      }
   172  
   173      let header: string = null;
   174      if (_.isNaN(parseInt(nodeId, 10))) {
   175        header = "Local Node";
   176      } else {
   177        header = `Node ${nodeId}`;
   178      }
   179  
   180      return (
   181        <Fragment>
   182          <h2 className="base-heading">{header} certificates</h2>
   183          {
   184            _.map(certificates.certificates, (cert, key) => (
   185              this.renderCert(cert, key)
   186            ))
   187          }
   188        </Fragment>
   189      );
   190    }
   191  
   192    render() {
   193      return (
   194        <div className="section">
   195          <Helmet title="Certificates | Debug" />
   196          <h1 className="base-heading">Certificates</h1>
   197  
   198          <section className="section">
   199            <Loading
   200              loading={!this.props.certificates}
   201              error={this.props.lastError}
   202              render={this.renderContent}
   203            />
   204          </section>
   205        </div>
   206      );
   207    }
   208  }
   209  
   210  const mapStateToProps = (state: AdminUIState, props: CertificatesProps) => {
   211    const nodeIDKey = certificatesRequestKey(certificatesRequestFromProps(props));
   212    return {
   213      certificates: state.cachedData.certificates[nodeIDKey] && state.cachedData.certificates[nodeIDKey].data,
   214      lastError: state.cachedData.certificates[nodeIDKey] && state.cachedData.certificates[nodeIDKey].lastError,
   215    };
   216  };
   217  
   218  const mapDispatchToProps = {
   219    refreshCertificates,
   220  };
   221  
   222  export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Certificates));