diff --git a/frontend/src/components/federation-create.tsx b/frontend/src/components/federation-create.tsx index 6e8fa6fd..3956f8cb 100644 --- a/frontend/src/components/federation-create.tsx +++ b/frontend/src/components/federation-create.tsx @@ -7,6 +7,9 @@ import { Accordion, AccordionItem, ToastNotification, + ModalWrapper, + TextInput, + Link } from 'carbon-components-react'; import { ToastContainer } from 'react-toastify'; import GetApiServerUri from './helpers'; @@ -17,7 +20,7 @@ import { } from 'redux/actions'; import { RootState } from 'redux/reducers'; import { link } from './types'; -import { Launch } from '@carbon/icons-react'; +import { Launch, NextOutline } from '@carbon/icons-react'; type FederationCreateProps = { tornjakMessageFunc: (globalErrorMessage: string) => void, @@ -26,18 +29,34 @@ type FederationCreateProps = { globalErrorMessage: string, }; +type Federation = { + trust_domain?: string, + federation_relationships?: { + trust_domain?: string, + bundle_endpoint_url?: string, + [key: string]: any + }[], + [key: string]: any +}; + type FederationCreateState = { federationJson: string, + uploadedFederation: Federation | Federation[], + federationLoaded: boolean, loading: boolean, statusOK: string, successJsonMessage: string, message: string, + exposedBundleEndpoint: string, + newFederationsIds: { trustDomain: string }[], + selectedFederationId: number, + federationSelected: boolean, }; const NewFederationJsonFormatLink = (props: { link: link }) => (
(Click to see new entry JSON format) - {} +
); @@ -46,13 +65,21 @@ class FederationCreate extends Component) { @@ -63,11 +90,39 @@ class FederationCreate extends Component { const result = evt.target?.result; if (typeof result === 'string') { - this.setState({ - federationJson: result, - message: "", - statusOK: "", - }); + try { + const parsed = JSON.parse(result); + const uploadedFederation = parsed; + + let newFederationsIds: { trustDomain: string }[]; + if (Array.isArray(uploadedFederation)) { + newFederationsIds = uploadedFederation.map((fed: Federation) => { + const trustDomain = (fed.federation_relationships && fed.federation_relationships.length > 0) + ? fed.federation_relationships[0].trust_domain || "No Trust Domain" + : "No Trust Domain"; + return { trustDomain: trustDomain }; + }); + } else { + const singleFed = uploadedFederation as Federation; + const trustDomain = (singleFed.federation_relationships && singleFed.federation_relationships.length > 0) + ? singleFed.federation_relationships[0].trust_domain || "No Trust Domain" + : "No Trust Domain"; + newFederationsIds = [{ + trustDomain: trustDomain + }]; + } + + this.setState({ + federationJson: result, + uploadedFederation: uploadedFederation, + federationLoaded: true, + message: "", + statusOK: "", + newFederationsIds: newFederationsIds, + }); + } catch (err) { + this.setState({ message: "Invalid JSON format.", statusOK: "ERROR" }); + } } }; reader.onerror = () => { @@ -77,19 +132,21 @@ class FederationCreate extends Component 0 + ? fed.federation_relationships[0] + : null; + + const exposedBundleEndpoint = fedRel?.bundle_endpoint_url || ""; + + this.setState({ + selectedFederationId: index, + federationSelected: true, + exposedBundleEndpoint: exposedBundleEndpoint, + }); + } + + applyEditToFederation() { + const { selectedFederationId, uploadedFederation, exposedBundleEndpoint } = this.state; + + if (selectedFederationId === -1) { + alert("Please select a Federation from the list, and make necessary changes to apply edit!"); + return false; + } + + let updatedFederations: Federation[]; + if (Array.isArray(uploadedFederation)) { + updatedFederations = [...uploadedFederation]; + } else { + updatedFederations = [uploadedFederation]; + } + + if ( + selectedFederationId >= 0 && + selectedFederationId < updatedFederations.length && + updatedFederations[selectedFederationId].federation_relationships && + updatedFederations[selectedFederationId].federation_relationships!.length > 0 + ) { + const cleanedUrl = exposedBundleEndpoint.trim(); + updatedFederations[selectedFederationId].federation_relationships![0].bundle_endpoint_url = cleanedUrl; + } else { + console.warn("No federation_relationships to update, or index out of range."); + } + + let finalUploadedFederation: Federation | Federation[] = updatedFederations; + if (updatedFederations.length === 1 && !Array.isArray(this.state.uploadedFederation)) { + finalUploadedFederation = updatedFederations[0]; + } + + this.setState({ + uploadedFederation: finalUploadedFederation, + selectedFederationId: -1, + federationSelected: false, + exposedBundleEndpoint: "", + }); + + console.log("Updated Federation JSON:", finalUploadedFederation); + alert(`Federation ${selectedFederationId + 1} Updated!`); + return true; + } + render() { - const { federationJson, loading, statusOK, message, successJsonMessage } = this.state; + const { loading, statusOK, message, successJsonMessage, federationLoaded, uploadedFederation, newFederationsIds, selectedFederationId, federationSelected, exposedBundleEndpoint } = this.state; const newFederationFormatLink = "https://github.com/spiffe/tornjak/blob/main/docs/newFederation-json-format.md"; return ( @@ -154,7 +286,7 @@ class FederationCreate extends Component - Upload Federation JSON} open> + Upload Federation Trust Bundle} open>
Choose your local file:

only .json files

@@ -168,16 +300,136 @@ class FederationCreate extends Component + {federationLoaded && ( +
+
+ +
+ {Array.isArray(uploadedFederation) && uploadedFederation.length === 1 && ( +
1 Federation Loaded from File
+ )} + {Array.isArray(uploadedFederation) && uploadedFederation.length > 1 && ( +
{uploadedFederation.length} Federations Loaded from File
+ )} + {!Array.isArray(uploadedFederation) && ( +
1 Federation Loaded from File
+ )} + + } + timeout={60000} + title="Federations Upload Notification" + /> + +
+ true} + size='lg' + triggerButtonKind="ghost" + buttonTriggerText="View Uploaded Federation(s)" + modalHeading="Federation JSON" + modalLabel="View Uploaded Federation(s)" + > +
{JSON.stringify(uploadedFederation, null, 2)}
+
+
+ +
+ +
+
+
+ Step 1. SELECT FEDERATION + {newFederationsIds.map((fedId, index) => ( +
+ {index === selectedFederationId && +
+ { + this.setSelectedFederation(index); + e.preventDefault(); + }} + > + {(index + 1).toString() + ". " + fedId.trustDomain} + +
+ } + {index !== selectedFederationId && +
+ { + this.setSelectedFederation(index); + e.preventDefault(); + }} + > + {(index + 1).toString() + ". " + fedId.trustDomain} + +
+ } +
+ ))} +
+
+ [Select Federation to Edit] +
+
+

Step 2. EDIT FEDERATION

+ this.setState({ exposedBundleEndpoint: e.target.value })} + /> +
+
+
+
+
+ )} +
- {!federationJson && ( + {((!Array.isArray(uploadedFederation) && !uploadedFederation) || (Array.isArray(uploadedFederation) && uploadedFederation.length === 0)) && (

(Upload JSON File to Enable)

)} diff --git a/frontend/src/components/style.css b/frontend/src/components/style.css index c266307c..54fc19c9 100644 --- a/frontend/src/components/style.css +++ b/frontend/src/components/style.css @@ -475,7 +475,7 @@ border-color: rgb(180, 178, 178); } -.view_entries_yaml_button { +.view_entries_yaml_button, .view_federations_yaml_button { display: inline-block; } @@ -644,6 +644,8 @@ .bundle-input-field { width: 400px; + margin-top: 10px; + margin-bottom: 15px; } .bundletitle{ diff --git a/frontend/src/components/trustbundle-create.tsx b/frontend/src/components/trustbundle-create.tsx index b9ab2745..b0ce7953 100644 --- a/frontend/src/components/trustbundle-create.tsx +++ b/frontend/src/components/trustbundle-create.tsx @@ -1,6 +1,6 @@ import { Component } from 'react'; import axios from 'axios'; -import { InlineNotification, TextArea, Button } from 'carbon-components-react'; +import { InlineNotification, TextArea, Button, TextInput } from 'carbon-components-react'; import { ToastContainer } from 'react-toastify'; import GetApiServerUri from './helpers'; import TornjakApi from './tornjak-api-helpers'; @@ -46,7 +46,7 @@ class TrustBundleCreate extends Component

Obtain Trust Bundle

-
- - - + this.handleInputChange(e as React.ChangeEvent)} className="bundle-input-field" />