diff --git a/tornjak-frontend/src/components/dashboard/agents-dashboard-table.js b/tornjak-frontend/src/components/dashboard/agents-dashboard-table.js
index f05c0340..a51cb204 100644
--- a/tornjak-frontend/src/components/dashboard/agents-dashboard-table.js
+++ b/tornjak-frontend/src/components/dashboard/agents-dashboard-table.js
@@ -76,12 +76,11 @@ class AgentDashboardTable extends React.Component {
}
agentList() {
- if ((typeof this.props.globalEntries.globalEntriesList === 'undefined') ||
- (typeof this.props.globalAgents.globalAgentsList === 'undefined')) {
+ if ((typeof this.props.globalAgents.globalAgentsList === 'undefined')) {
return [];
}
- let agentEntriesDict = this.SpiffeHelper.getAgentsEntries(this.props.globalAgents.globalAgentsList, this.props.globalEntries.globalEntriesList)
+ let agentEntriesDict = this.SpiffeHelper.getAgentsEntries(this.props.globalAgents.globalAgentsList, this.props.globalEntries.globalEntriesList) // TODO this.props.globalEntries.globalEntriesList may be undefined if there are no entries
return this.props.globalAgents.globalAgentsList.map(currentAgent => {
return this.getChildEntries(currentAgent, agentEntriesDict);
})
diff --git a/tornjak-frontend/src/components/entry-create.js b/tornjak-frontend/src/components/entry-create.js
index c2c41df3..60db40d0 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,
@@ -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 });
}
@@ -605,17 +671,80 @@ class CreateEntry extends Component {
/>
-
+
+
+
+ {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
+ }
+
+ }
+
e.parent_id.path === "/spire/server");
var lambdas = [];
var agentEntriesDict = {};
diff --git a/tornjak-frontend/src/components/style.css b/tornjak-frontend/src/components/style.css
index 306accd0..506ffd36 100644
--- a/tornjak-frontend/src/components/style.css
+++ b/tornjak-frontend/src/components/style.css
@@ -13,152 +13,179 @@
.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;
+}
+