From 82d43a0ffc139d1d6d498eb4b1c9fcd1647bfada Mon Sep 17 00:00:00 2001 From: Mohammed Abdi Date: Mon, 23 Aug 2021 11:13:26 -1000 Subject: [PATCH] workload-entry-expiry-features repo move to public with new commits Signed-off-by: Mohammed Abdi --- .../src/components/entry-create.js | 197 +++++++++-- tornjak-frontend/src/components/style.css | 312 ++++++++++-------- 2 files changed, 332 insertions(+), 177 deletions(-) diff --git a/tornjak-frontend/src/components/entry-create.js b/tornjak-frontend/src/components/entry-create.js index c2c41df3..d96f3cc4 100644 --- a/tornjak-frontend/src/components/entry-create.js +++ b/tornjak-frontend/src/components/entry-create.js @@ -32,7 +32,13 @@ class CreateEntry extends Component { this.prepareSelectorsList = this.prepareSelectorsList.bind(this); this.onChangeSelectorsRecommended = this.onChangeSelectorsRecommended.bind(this); this.onChangeTtl = this.onChangeTtl.bind(this); - this.onChangeExpiresAt = this.onChangeExpiresAt.bind(this); + this.onChangeExpiryOption = this.onChangeExpiryOption.bind(this); + this.expiryTimeUpdate = this.expiryTimeUpdate.bind(this); + this.onChangeExpiresAtSeconds = this.onChangeExpiresAtSeconds.bind(this); + this.isValidDate = this.isValidDate.bind(this); + this.updateValidDateAndTime = this.updateValidDateAndTime.bind(this); + this.onChangeExpiresAtTime = this.onChangeExpiresAtTime.bind(this); + this.onChangeExpiresAtDate = this.onChangeExpiresAtDate.bind(this); this.onChangeFederatesWith = this.onChangeFederatesWith.bind(this); this.onChangeDownStream = this.onChangeDownStream.bind(this); this.onChangeDnsNames = this.onChangeDnsNames.bind(this); @@ -57,7 +63,13 @@ class CreateEntry extends Component { adminFlag: false, ttl: 0, + expiryOption: "None", + expiryOptionList: ["None", "Seconds Since Epoch", "Date/Time"], + expiryDate: "1/1/2021", + expiryTime: "00:00", expiresAt: 0, + expiryUnsafe: false, + expiryInvalid: false, dnsNames: [], federatesWith: [], downstream: false, @@ -116,7 +128,7 @@ class CreateEntry extends Component { this.prepareParentIdAgentsList(); this.prepareSelectorsList(); } - if (prevState.parentId !== this.state.parentId) { + if (prevState.parentId !== this.state.parentId) { this.prepareSelectorsList(); } } @@ -140,13 +152,13 @@ class CreateEntry extends Component { let agentSpiffeid = this.SpiffeHelper.getAgentSpiffeid(this.props.globalAgentsList[i]); localAgentsIdList[idx] = agentSpiffeid; idx++; - + // Add entries associated with this agent let agentEntries = agentEntriesDict[agentSpiffeid]; if (agentEntries !== undefined) { - for (let j=0; j < agentEntries.length; j++) { - localAgentsIdList[idx] = this.SpiffeHelper.getEntrySpiffeid(agentEntries[j]); - idx++; + for (let j = 0; j < agentEntries.length; j++) { + localAgentsIdList[idx] = this.SpiffeHelper.getEntrySpiffeid(agentEntries[j]); + idx++; } } } @@ -171,14 +183,14 @@ class CreateEntry extends Component { let agentId = parentId; // Check if parent ID is not canonical ID, best effort try to match it to an Agent ID for selectors if (!this.props.globalAgentsList.map(e => this.SpiffeHelper.getAgentSpiffeid(e)).includes(parentId)) { - let fEntries= this.props.globalEntriesList.filter(e => this.SpiffeHelper.getEntrySpiffeid(e) === parentId); - if (fEntries.length > 0) { - let entry = fEntries[0]; - let canonicalAgentId = this.SpiffeHelper.getCanonicalAgentSpiffeid(entry, this.props.globalAgentsList) - if (canonicalAgentId !== "") { - agentId = canonicalAgentId; - } + let fEntries = this.props.globalEntriesList.filter(e => this.SpiffeHelper.getEntrySpiffeid(e) === parentId); + if (fEntries.length > 0) { + let entry = fEntries[0]; + let canonicalAgentId = this.SpiffeHelper.getCanonicalAgentSpiffeid(entry, this.props.globalAgentsList) + if (canonicalAgentId !== "") { + agentId = canonicalAgentId; } + } } for (let i = 0; i < globalAgentsWorkLoadAttestorInfo.length; i++) { @@ -204,12 +216,65 @@ class CreateEntry extends Component { }); } - onChangeExpiresAt(e) { + onChangeExpiryOption(e) { this.setState({ - expiresAt: Number(e.imaginaryTarget.value) + expiresAt: 0, + expiryOption: e.selectedItem, + expiryUnsafe: false, + expiryInvalid: false }); } + isValidExpiryTime(seconds) { + const JSMaxSafeTime = 8640000000000 // JS cannot represent times safely larger than this + return seconds > 0 && seconds <= JSMaxSafeTime + } + + expiryTimeUpdate(seconds) { + this.setState({ + expiresAt: seconds, + expiryUnsafe: !this.isValidExpiryTime(seconds) + }) + } + + onChangeExpiresAtSeconds(e) { + var seconds = Number(e.imaginaryTarget.value) + this.expiryTimeUpdate(seconds) + } + + // TODO some odd behavior with dates like February 33 exists + isValidDate(d) { // date is successfully translated in Javascript + return d instanceof Date && isFinite(d) + } + + updateValidDateAndTime(d, t) { + var testDate = new Date(d + ", " + t) + this.setState({ // should always reflect what the user sees + expiryDate: d, + expiryTime: t + }) + if (this.isValidDate(testDate)) { + this.setState({ + expiryInvalid: false, + }) + var seconds = Math.round(testDate / 1000) + this.expiryTimeUpdate(seconds) + console.log(d, t, this.state.expiryDate, this.state.expiryTime) + return + } + this.setState({ + expiryInvalid: true, + }) + } + + onChangeExpiresAtDate(e) { + this.updateValidDateAndTime(e.target.value, this.state.expiryTime) + } + + onChangeExpiresAtTime(e) { + this.updateValidDateAndTime(this.state.expiryDate, e.target.value) + } + onChangeDownStream = selected => { var sid = selected; this.setState({ @@ -408,6 +473,7 @@ class CreateEntry extends Component { newTags.splice(currPos, 1); newTags.splice(newPos, 0, tag); + // re-render this.setState({ tags: newTags }); } @@ -490,17 +556,17 @@ class CreateEntry extends Component { } axios.post(endpoint, cjtData) .then( - res => this.setState({ + res => this.setState({ message: "Request:" + JSON.stringify(cjtData, null, ' ') + "\n\nSuccess:" + JSON.stringify(res.data, null, ' '), statusOK: "OK", successJsonMessege: res.data.results[0].status.message }) ) .catch( - err => this.setState({ + err => this.setState({ message: "ERROR:" + err, statusOK: "ERROR" - }) + }) ) } @@ -558,8 +624,8 @@ class CreateEntry extends Component { e.target.value = this.state.spiffeIdPrefix + input.substr(this.state.spiffeIdPrefix.length); this.onChangeSpiffeId(e); }} - //onChange={this.onChangeSpiffeId} - required/> + //onChange={this.onChangeSpiffeId} + required />
- +
+ +
+ {this.state.expiryOption !== "None" &&
+ {this.state.expiryOption === "Seconds Since Epoch" && +
+
+ +
+
+ } + {this.state.expiryOption === "Date/Time" && +
+
+ +
+
+ +
+
+ } +
+ + + } + {(this.state.expiryUnsafe || this.state.expiryInvalid) && +
+

Warning: expiry time either in invalid format, is negative, or is too large. Submitting this time may result in undefined behavior.

+ {this.state.expiryOption === "Seconds Since Epoch" && this.state.expiryUnsafe && +

Seconds must be positive and less than 8640000000000

+ } + {this.state.expiryOption === "Date/Time" && this.state.expiryUnsafe && +

Date must be past January 1, 1970 @ 12:00AM GMT

+ } + {this.state.expiryOption === "Date/Time" && this.state.expiryInvalid && +

Date or time format is invalid

+ } +
+ } +
- {this.state.statusOK==="OK" && this.state.successJsonMessege==="OK" && + {this.state.statusOK === "OK" && this.state.successJsonMessege === "OK" &&

--ENTRY SUCCESSFULLY CREATED--

} - {(this.state.statusOK==="ERROR" || (this.state.successJsonMessege !== "OK" && this.state.successJsonMessege !== "")) && + {(this.state.statusOK === "ERROR" || (this.state.successJsonMessege !== "OK" && this.state.successJsonMessege !== "")) &&

--ENTRY CREATION FAILED--

}
@@ -692,4 +821,4 @@ const mapStateToProps = (state) => ({ export default connect( mapStateToProps, { serverSelectedFunc, agentworkloadSelectorInfoFunc, selectorInfoFunc, agentsListUpdateFunc, entriesListUpdateFunc, tornjakMessageFunc, tornjakServerInfoUpdateFunc, serverInfoUpdateFunc } -)(CreateEntry) +)(CreateEntry) \ No newline at end of file diff --git a/tornjak-frontend/src/components/style.css b/tornjak-frontend/src/components/style.css index 306accd0..6d504dc0 100644 --- a/tornjak-frontend/src/components/style.css +++ b/tornjak-frontend/src/components/style.css @@ -13,152 +13,178 @@ .dropdown-container { margin-left: 200px; } + .dropbtn { color: white; padding: 30px; font-size: 16px; border: none; - } - - .dropdown { - position: relative; - display: inline-block; - padding: 15px; - } - - .dropdown-content { - margin-top: 10px; - position: absolute; - background-color: #f1f1f1; - display: none; - min-width: 130px; - box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); - z-index: 3; - } - - .dropdown-content a { - color: black; - padding: 12px 16px; - z-index: 3; - } - - .dropdown-content a:hover {background-color: #ddd;} - .dropdown:hover .dropdown-content {display: block;} - .dropdown:hover .dropbtn {color: #ffffff; text-decoration: none;} - - .indvidual-list-table { - margin-top: 10px; - width: 98%; - margin-bottom: 20px; - } - - .create-entry-title { - margin-left: auto; - margin-right: auto; - } - - .entry-form { - margin-left: auto; - margin-right: auto; - width: 60%; - border-width:1px; - border-style:solid; - border-color: rgb(180, 178, 178); - padding: 30px 30px 30px 30px; - } - .parentId-drop-down { - margin-bottom: 20px; - } - - .clustertype-drop-down { - margin-bottom: 20px; - } - - .parentId-manual-input-field { - margin-bottom: 20px; - margin-left: 40px; - } - - .clustertype-manual-input-field { - margin-bottom: 20px; - margin-left: 40px; - } - - .spiffeId-input-field { - margin-bottom: 20px; - } - - .cluster-domain-name-input-field { - margin-bottom: 20px; - } - - .cluster-managed-by-input-field { - margin-bottom: 20px; - } - - .clustername-input-field { - margin-bottom: 20px; - } - - .parentId-helper { - font-size: 12px; - color:rgb(82, 79, 79); - margin-top: 2px; - } - - .cluster-helper { - font-size: 12px; - color:rgb(82, 79, 79); - margin-top: 2px; - } - .selectors-multiselect { - margin-bottom: 20px; - } - - .agents-multiselect { - margin-bottom: 20px; - } - .selectors-textArea { - margin-bottom: 20px; - } - - .ttl-input { - display: inline-block; - margin-right: 20px; - margin-bottom: 20px; - width: 160px - } - - .expiresAt-input { - display: inline-block; - margin-bottom: 20px; - width: 160px; - } +} - .federates-with-input-field { - margin-bottom: 20px; - width: 55%; - } - .dnsnames-input-field { - margin-bottom: 20px; - width: 55%; - } - - .servers-drp-dwn { - margin-top: 5px; - } - - .success-message { - font-weight: bold; - color: green; - } - - .failed-message { - font-weight: bold; - color:red; - } - - .no-data { - margin-top: 120px; - margin-left: 200px; - } \ No newline at end of file +.dropdown { + position: relative; + display: inline-block; + padding: 15px; +} + +.dropdown-content { + margin-top: 10px; + position: absolute; + background-color: #f1f1f1; + display: none; + min-width: 130px; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + z-index: 3; +} + +.dropdown-content a { + color: black; + padding: 12px 16px; + z-index: 3; +} + +.dropdown-content a:hover {background-color: #ddd;} +.dropdown:hover .dropdown-content {display: block;} +.dropdown:hover .dropbtn {color: #ffffff; text-decoration: none;} + +.indvidual-list-table { + margin-top: 10px; + width: 98%; + margin-bottom: 20px; +} + +.create-entry-title { + margin-left: auto; + margin-right: auto; +} + +.entry-form { + margin-left: auto; + margin-right: auto; + width: 60%; + border-width:1px; + border-style:solid; + border-color: rgb(180, 178, 178); + padding: 30px 30px 30px 30px; +} +.parentId-drop-down { + margin-bottom: 20px; +} + +.clustertype-drop-down { + margin-bottom: 20px; +} + +.parentId-manual-input-field { + margin-bottom: 20px; + margin-left: 40px; +} + +.clustertype-manual-input-field { + margin-bottom: 20px; + margin-left: 40px; +} + +.spiffeId-input-field { + margin-bottom: 20px; +} + +.cluster-domain-name-input-field { + margin-bottom: 20px; +} + +.cluster-managed-by-input-field { + margin-bottom: 20px; +} + +.clustername-input-field { + margin-bottom: 20px; +} + +.parentId-helper { + font-size: 12px; + color:rgb(82, 79, 79); + margin-top: 2px; +} + +.cluster-helper { + font-size: 12px; + color:rgb(82, 79, 79); + margin-top: 2px; +} +.selectors-multiselect { + margin-bottom: 20px; +} + +.agents-multiselect { + margin-bottom: 20px; +} +.selectors-textArea { + margin-bottom: 20px; +} + +.ttl-input { + margin-right: 20px; + margin-bottom: 20px; + width: 200px +} + +.expiresAt-input { + margin-right: 20px; + margin-bottom: 20px; + width: 600px; +} + +.expiry-drop-down { + display: inline-block; + vertical-align: top; + margin-right: 20px; + width: 200px; +} + +.expiryEntryFields { + display: inline-block; + vertical-align: top; + width: 200px; +} + +.expiryOption-field { + display: inline-block; + vertical-align: top; + width: 500px; +} + +.expiryOption-entry { + margin-right: 20px; + display: inline-block; + vertical-align: top; + width: 200px; +} + +.federates-with-input-field { + margin-bottom: 20px; + width: 420px; +} +.dnsnames-input-field { + margin-bottom: 20px; + width: 420px; +} + +.servers-drp-dwn { + margin-top: 5px; +} + +.success-message { + font-weight: bold; + color: green; +} + +.failed-message { + font-weight: bold; + color:red; +} + +.no-data { + margin-top: 120px; + margin-left: 200px; +} \ No newline at end of file