From d401effc1c6a4f840d12de1aba7ad81b166c129a Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Thu, 24 Oct 2024 17:02:13 +0100 Subject: [PATCH 1/2] [HAL-1992] Add support for component certificates --- .../client/installer/CertificateColumn.java | 177 ++++++++++++++++++ .../hal/client/installer/CertificateInfo.java | 44 +++++ .../client/installer/CertificatePreview.java | 46 +++++ .../ConfirmComponentCertificateStep.java | 116 ++++++++++++ .../ImportComponentCertificateContext.java | 40 ++++ .../ImportComponentCertificateState.java | 20 ++ .../ImportComponentCertificateWizard.java | 89 +++++++++ .../client/installer/UpdateManagerColumn.java | 5 + .../installer/UpdateManagerResources.java | 3 + .../UploadComponentCertificateStep.java | 111 +++++++++++ .../hal/client/installer/certificate.base64 | 1 + .../hal/client/installer/certificate.dmr | 26 +++ .../hal/dmr/ModelDescriptionConstants.java | 7 + .../org/jboss/hal/resources/Constants.java | 3 + .../java/org/jboss/hal/resources/Ids.java | 3 + .../org/jboss/hal/resources/Messages.java | 4 + .../java/org/jboss/hal/resources/Names.java | 2 + .../org/jboss/hal/resources/Previews.java | 4 + .../jboss/hal/resources/Constants.properties | 3 + .../jboss/hal/resources/Messages.properties | 4 + .../previews/update-manager/certificates.html | 3 + 21 files changed, 711 insertions(+) create mode 100644 app/src/main/java/org/jboss/hal/client/installer/CertificateColumn.java create mode 100644 app/src/main/java/org/jboss/hal/client/installer/CertificateInfo.java create mode 100644 app/src/main/java/org/jboss/hal/client/installer/CertificatePreview.java create mode 100644 app/src/main/java/org/jboss/hal/client/installer/ConfirmComponentCertificateStep.java create mode 100644 app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateContext.java create mode 100644 app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateState.java create mode 100644 app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateWizard.java create mode 100644 app/src/main/java/org/jboss/hal/client/installer/UploadComponentCertificateStep.java create mode 100644 app/src/main/java/org/jboss/hal/client/installer/certificate.base64 create mode 100644 app/src/main/java/org/jboss/hal/client/installer/certificate.dmr create mode 100644 resources/src/main/resources/org/jboss/hal/resources/previews/update-manager/certificates.html diff --git a/app/src/main/java/org/jboss/hal/client/installer/CertificateColumn.java b/app/src/main/java/org/jboss/hal/client/installer/CertificateColumn.java new file mode 100644 index 0000000000..08895c819a --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/CertificateColumn.java @@ -0,0 +1,177 @@ +/* + * Copyright 2024 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.hal.client.installer; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.jboss.hal.ballroom.dialog.DialogFactory; +import org.jboss.hal.core.finder.ColumnAction; +import org.jboss.hal.core.finder.ColumnActionFactory; +import org.jboss.hal.core.finder.Finder; +import org.jboss.hal.core.finder.FinderColumn; +import org.jboss.hal.core.finder.ItemAction; +import org.jboss.hal.core.finder.ItemDisplay; +import org.jboss.hal.dmr.Operation; +import org.jboss.hal.dmr.ResourceAddress; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.meta.security.Constraint; +import org.jboss.hal.resources.CSS; +import org.jboss.hal.resources.Icons; +import org.jboss.hal.resources.Ids; +import org.jboss.hal.resources.Names; +import org.jboss.hal.resources.Resources; +import org.jboss.hal.resources.UIConstants; +import org.jboss.hal.spi.Callback; +import org.jboss.hal.spi.Column; +import org.jboss.hal.spi.Message; +import org.jboss.hal.spi.MessageEvent; +import org.jboss.hal.spi.Requires; + +import com.google.web.bindery.event.shared.EventBus; + +import elemental2.dom.HTMLElement; +import elemental2.promise.Promise; + +import static java.util.stream.Collectors.toList; + +import static org.jboss.hal.client.installer.AddressTemplates.INSTALLER_ADDRESS; +import static org.jboss.hal.client.installer.AddressTemplates.INSTALLER_TEMPLATE; +import static org.jboss.hal.core.finder.FinderColumn.RefreshMode.CLEAR_SELECTION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERTIFICATES; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERTIFICATE_REMOVE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.INCLUDE_RUNTIME; +import static org.jboss.hal.dmr.ModelDescriptionConstants.KEY_ID; +import static org.jboss.hal.dmr.ModelDescriptionConstants.READ_RESOURCE_OPERATION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; + +@Column(Ids.UPDATE_MANAGER_CERTIFICATE) +@Requires(INSTALLER_ADDRESS) +public class CertificateColumn extends FinderColumn { + + private final Resources resources; + private final EventBus eventBus; + private final Dispatcher dispatcher; + private final StatementContext statementContext; + + @Inject + public CertificateColumn(final Finder finder, + final EventBus eventBus, + final ColumnActionFactory columnActionFactory, + final StatementContext statementContext, + final Dispatcher dispatcher, + final Resources resources) { + super(new Builder(finder, Ids.UPDATE_MANAGER_CERTIFICATE, Names.CERTIFICATES) + .onPreview((CertificateInfo c) -> new CertificatePreview(c)) + .showCount() + .withFilter()); + + this.eventBus = eventBus; + this.dispatcher = dispatcher; + this.statementContext = statementContext; + this.resources = resources; + + setItemsProvider(context -> { + ResourceAddress address = INSTALLER_TEMPLATE.resolve(statementContext); + Operation operation = new Operation.Builder(address, READ_RESOURCE_OPERATION) + .param(INCLUDE_RUNTIME, true) + .build(); + return dispatcher.execute(operation).then(result -> { + // noinspection Convert2MethodRef + List certificates = result.get(CERTIFICATES).asList().stream() + .map(node -> new CertificateInfo(node)) + .collect(toList()); + return Promise.resolve(certificates); + }); + }); + + setItemRenderer(item -> new ItemDisplay() { + @Override + public String getId() { + return Ids.asId(item.getKeyID()); + } + + @Override + public String getTitle() { + return item.getDescription(); + } + + @Override + public HTMLElement getIcon() { + if (item.getStatus().equals(CertificateInfo.TRUSTED)) { + return Icons.ok(); + } else { + return Icons.warning(); + } + } + + @Override + public HTMLElement element() { + return ItemDisplay.withSubtitle(item.getDescription(), item.getKeyID()); + } + + @Override + public List> actions() { + List> actions = new ArrayList<>(); + actions.add(new ItemAction.Builder() + .title(resources.constants().remove()) + .handler(itm -> remove(itm)) + .constraint(Constraint.executable(INSTALLER_TEMPLATE, WRITE_ATTRIBUTE_OPERATION)) + .build()); + return actions; + } + }); + + addColumnAction(new ColumnAction.Builder(Ids.UPDATE_MANAGER_CERTIFICATE_ADD) + .element(columnActionFactory.addButton(resources.constants().importCertificate(), + CSS.pfIcon(UIConstants.ADD_CIRCLE_O))) + .handler(column -> add()) + .constraint(Constraint.executable(INSTALLER_TEMPLATE, WRITE_ATTRIBUTE_OPERATION)) + .build()); + addColumnAction(columnActionFactory.refresh(Ids.UPDATE_MANAGER_CERTIFICATE_REFRESH)); + } + + private void add() { + new ImportComponentCertificateWizard(dispatcher, statementContext, resources).show(this); + } + + private void remove(CertificateInfo certificate) { + DialogFactory.showConfirmation(resources.constants().removeComponentCertificate(), + resources.messages().removeUpdateCertificateQuestion(certificate.getKeyID()), + () -> removeCertificate(certificate.getKeyID(), () -> refresh(CLEAR_SELECTION))); + } + + void removeCertificate(final String name, final Callback callback) { + Operation operation = new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), CERTIFICATE_REMOVE) + .param(KEY_ID, name) + .build(); + dispatcher.execute(operation) + .then(result -> { + MessageEvent.fire(eventBus, Message.success(resources.messages().removeResourceSuccess(Names.CERTIFICATE, + name))); + return null; + }) + .catch_(failure -> { + MessageEvent.fire(eventBus, + Message.error(resources.messages().lastOperationFailed(), String.valueOf(failure))); + return null; + }) + .finally_(callback::execute); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/installer/CertificateInfo.java b/app/src/main/java/org/jboss/hal/client/installer/CertificateInfo.java new file mode 100644 index 0000000000..2807608a39 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/CertificateInfo.java @@ -0,0 +1,44 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.hal.client.installer; + +import org.jboss.hal.dmr.ModelNode; +import org.jboss.hal.dmr.NamedNode; + +public class CertificateInfo extends NamedNode { + + protected static final String TRUSTED = "TRUSTED"; + + CertificateInfo(final ModelNode node) { + super(node.get("key-id").asString(), node); + } + + String getKeyID() { + return get("key-id").asString(); + } + + public String getFingerprint() { + return get("fingerprint").asString(); + } + + public String getDescription() { + return get("description").asString(); + } + + public String getStatus() { + return get("status").asString(); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/installer/CertificatePreview.java b/app/src/main/java/org/jboss/hal/client/installer/CertificatePreview.java new file mode 100644 index 0000000000..f3c319b65b --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/CertificatePreview.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.hal.client.installer; + +import org.jboss.hal.ballroom.LabelBuilder; +import org.jboss.hal.core.finder.PreviewAttributes; +import org.jboss.hal.core.finder.PreviewContent; +import org.jboss.hal.dmr.ModelDescriptionConstants; + +import static org.jboss.hal.dmr.ModelDescriptionConstants.DESCRIPTION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.KEY_ID; +import static org.jboss.hal.dmr.ModelDescriptionConstants.STATUS; + +class CertificatePreview extends PreviewContent { + + CertificatePreview(CertificateInfo certificate) { + super(certificate.getKeyID()); + + LabelBuilder labelBuilder = new LabelBuilder(); + PreviewAttributes attributes = new PreviewAttributes<>(certificate); + attributes + .append(model -> new PreviewAttributes.PreviewAttribute(labelBuilder.label(KEY_ID), certificate.getKeyID())); + attributes.append( + model -> new PreviewAttributes.PreviewAttribute(labelBuilder.label(ModelDescriptionConstants.FINGERPRINT), + certificate.getFingerprint())); + attributes.append( + model -> new PreviewAttributes.PreviewAttribute(labelBuilder.label(DESCRIPTION), certificate.getDescription())); + attributes + .append(model -> new PreviewAttributes.PreviewAttribute(labelBuilder.label(STATUS), certificate.getStatus())); + + previewBuilder().addAll(attributes); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/installer/ConfirmComponentCertificateStep.java b/app/src/main/java/org/jboss/hal/client/installer/ConfirmComponentCertificateStep.java new file mode 100644 index 0000000000..aacab076d7 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/ConfirmComponentCertificateStep.java @@ -0,0 +1,116 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.hal.client.installer; + +import java.util.Collections; +import java.util.List; + +import org.jboss.hal.ballroom.wizard.AsyncStep; +import org.jboss.hal.ballroom.wizard.WizardStep; +import org.jboss.hal.ballroom.wizard.WorkflowCallback; +import org.jboss.hal.core.mbui.form.ModelNodeForm; +import org.jboss.hal.dmr.ModelNode; +import org.jboss.hal.dmr.Operation; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.flow.FlowContext; +import org.jboss.hal.flow.Progress; +import org.jboss.hal.flow.Task; +import org.jboss.hal.meta.Metadata; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.resources.Ids; +import org.jboss.hal.resources.Resources; + +import elemental2.dom.HTMLElement; +import elemental2.promise.Promise; + +import static org.jboss.elemento.Elements.div; +import static org.jboss.hal.client.installer.AddressTemplates.INSTALLER_TEMPLATE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERTIFICATE_IMPORT; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERT_FILE; +import static org.jboss.hal.flow.Flow.sequential; +import static org.jboss.hal.resources.CSS.marginBottomLarge; + +class ConfirmComponentCertificateStep + extends WizardStep + implements AsyncStep { + + private final Dispatcher dispatcher; + private final StatementContext statementContext; + private final Resources resources; + + public ConfirmComponentCertificateStep(final Dispatcher dispatcher, + final StatementContext statementContext, + final Resources resources) { + super(resources.constants().confirmation()); + + this.dispatcher = dispatcher; + this.statementContext = statementContext; + this.resources = resources; + + table = new ModelNodeForm.Builder(Ids.build(Ids.UPDATE_MANAGER_LIST_UPDATES), + Metadata.staticDescription(UpdateManagerResources.INSTANCE.componentCertificate())) + .readOnly() + .build(); + registerAttachable(table); + + root = div() + .add(div().css(marginBottomLarge).innerHtml(resources.messages().importComponentCertificateConfirmation())) + .add(table) + .element(); + } + + private final HTMLElement root; + private final ModelNodeForm table; + + @Override + public HTMLElement element() { + return root; + } + + @Override + protected void onShow(final ImportComponentCertificateContext context) { + table.view(context.getImportedCertificate()); + } + + @Override + public void onNext(final ImportComponentCertificateContext context, final WorkflowCallback callback) { + Operation operation = new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), CERTIFICATE_IMPORT) + .param(CERT_FILE, new ModelNode().set(0)) + .build(); + List> tasks = Collections.singletonList( + flowContext -> dispatcher.upload(context.getFile(), operation).then(result -> { + context.setImportedCertificate(new CertificateInfo(result.get())); + return Promise.resolve(flowContext); + })); + + sequential(new FlowContext(Progress.NOOP), tasks) + .timeout(Timeouts.UPLOAD * 1_000) + .then(flowContext -> { + callback.proceed(); + return Promise.resolve(context); + }) + .catch_(failure -> { + if (FlowContext.timeout(failure)) { + wizard().showError(resources.constants().timeout(), resources.messages().operationTimeout(), false); + } else { + wizard().showError(resources.constants().error(), + resources.messages().uploadError(context.getFile().name), + String.valueOf(failure), false); + } + return Promise.reject(failure); + }); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateContext.java b/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateContext.java new file mode 100644 index 0000000000..b888b13691 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateContext.java @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.hal.client.installer; + +import elemental2.dom.File; + +class ImportComponentCertificateContext { + + private CertificateInfo certificate; + private File file; + + public void setImportedCertificate(CertificateInfo certificate) { + this.certificate = certificate; + } + + public CertificateInfo getImportedCertificate() { + return certificate; + } + + public void setFile(File file) { + this.file = file; + } + + public File getFile() { + return file; + } +} diff --git a/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateState.java b/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateState.java new file mode 100644 index 0000000000..7b77bd3007 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateState.java @@ -0,0 +1,20 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.hal.client.installer; + +enum ImportComponentCertificateState { + UPLOAD_CERTIFICATE, CONFIRM_CERTIFICATE +} diff --git a/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateWizard.java b/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateWizard.java new file mode 100644 index 0000000000..79d7e32a13 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateWizard.java @@ -0,0 +1,89 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.hal.client.installer; + +import org.jboss.hal.ballroom.wizard.Wizard; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.resources.Resources; + +import static org.jboss.hal.client.installer.ImportComponentCertificateState.CONFIRM_CERTIFICATE; +import static org.jboss.hal.client.installer.ImportComponentCertificateState.UPLOAD_CERTIFICATE; +import static org.jboss.hal.core.finder.FinderColumn.RefreshMode.CLEAR_SELECTION; + +class ImportComponentCertificateWizard { + private final Dispatcher dispatcher; + private final StatementContext statementContext; + private final Resources resources; + private final ImportComponentCertificateContext context; + + ImportComponentCertificateWizard( + final Dispatcher dispatcher, + final StatementContext statementContext, + final Resources resources) { + this.dispatcher = dispatcher; + this.statementContext = statementContext; + this.resources = resources; + this.context = new ImportComponentCertificateContext(); + } + + void show(CertificateColumn column) { + Wizard.Builder builder = new Wizard.Builder<>( + resources.constants().importCertificate(), context); + + builder.stayOpenAfterFinish() + .addStep(UPLOAD_CERTIFICATE, new UploadComponentCertificateStep( + dispatcher, + statementContext, + resources)) + .addStep(CONFIRM_CERTIFICATE, new ConfirmComponentCertificateStep( + dispatcher, + statementContext, + resources)); + + builder.onBack((ctx, currentState) -> { + ImportComponentCertificateState previous = null; + switch (currentState) { + case UPLOAD_CERTIFICATE: + break; + case CONFIRM_CERTIFICATE: + previous = UPLOAD_CERTIFICATE; + break; + } + return previous; + }); + + builder.onNext((ctx, currentState) -> { + ImportComponentCertificateState next = null; + switch (currentState) { + case UPLOAD_CERTIFICATE: + next = CONFIRM_CERTIFICATE; + break; + case CONFIRM_CERTIFICATE: + break; + } + return next; + }); + + builder.onFinish((wizard, ctx) -> { + column.refresh(CLEAR_SELECTION); + wizard.showSuccess(resources.constants().componentCertificateImported(), + resources.messages().componentCertificateImportedDescription()); + }); + + builder.build().show(); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerColumn.java b/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerColumn.java index affbd6a4ca..bdd6507f8a 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerColumn.java +++ b/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerColumn.java @@ -44,6 +44,11 @@ public UpdateManagerColumn(Finder finder, Resources resources) { .nextColumn(Ids.UPDATE_MANAGER_CHANNEL) .onPreview(new PreviewContent<>(resources.constants().channelDetails(), resources.previews().updateManagerChannels())) + .build(), + new StaticItem.Builder(Names.CERTIFICATES) + .nextColumn(Ids.UPDATE_MANAGER_CERTIFICATE) + .onPreview(new PreviewContent<>(resources.constants().updateCertificates(), + resources.previews().updateManagerCertificates())) .build())); } } diff --git a/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerResources.java b/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerResources.java index 2358be0208..fe1ac7acd1 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerResources.java +++ b/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerResources.java @@ -28,4 +28,7 @@ interface UpdateManagerResources extends ClientBundle { @Source("channelChange.base64") TextResource channelChange(); + + @Source("certificate.base64") + TextResource componentCertificate(); } diff --git a/app/src/main/java/org/jboss/hal/client/installer/UploadComponentCertificateStep.java b/app/src/main/java/org/jboss/hal/client/installer/UploadComponentCertificateStep.java new file mode 100644 index 0000000000..043faaa071 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/UploadComponentCertificateStep.java @@ -0,0 +1,111 @@ +/* + * Copyright 2022 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.hal.client.installer; + +import java.util.Collections; +import java.util.List; + +import org.jboss.hal.ballroom.wizard.AsyncStep; +import org.jboss.hal.ballroom.wizard.WizardStep; +import org.jboss.hal.ballroom.wizard.WorkflowCallback; +import org.jboss.hal.client.shared.uploadwizard.UploadElement; +import org.jboss.hal.dmr.ModelNode; +import org.jboss.hal.dmr.Operation; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.flow.FlowContext; +import org.jboss.hal.flow.Progress; +import org.jboss.hal.flow.Task; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.resources.Resources; + +import elemental2.dom.File; +import elemental2.dom.HTMLElement; +import elemental2.promise.Promise; + +import static org.jboss.elemento.Elements.div; +import static org.jboss.hal.client.installer.AddressTemplates.INSTALLER_TEMPLATE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERTIFICATE_PARSE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERT_FILE; +import static org.jboss.hal.flow.Flow.sequential; + +class UploadComponentCertificateStep extends WizardStep + implements AsyncStep { + + private final Dispatcher dispatcher; + private final StatementContext statementContext; + private final Resources resources; + private final HTMLElement root; + private final UploadElement uploadElement; + + UploadComponentCertificateStep(final Dispatcher dispatcher, + final StatementContext statementContext, + final Resources resources) { + super(resources.constants().importCertificate()); + this.statementContext = statementContext; + this.dispatcher = dispatcher; + this.resources = resources; + + root = div() + .add(uploadElement = new UploadElement(true, resources.messages().noContent())) + .element(); + } + + @Override + public HTMLElement element() { + return root; + } + + @Override + protected void onShow(ImportComponentCertificateContext context) { + uploadElement.reset(); + } + + @Override + public void onNext(final ImportComponentCertificateContext context, final WorkflowCallback callback) { + if (uploadElement.getFiles().length == 0) { + wizard().showError(resources.constants().error(), resources.messages().noFileSelected(), false); + } + + final File item = uploadElement.getFiles().item(0); + wizard().showProgress(resources.constants().uploadInProgress(), resources.messages().uploadInProgress(item.name)); + context.setFile(item); + + Operation operation = new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), CERTIFICATE_PARSE) + .param(CERT_FILE, new ModelNode().set(0)) + .build(); + List> tasks = Collections.singletonList( + flowContext -> dispatcher.upload(uploadElement.getFiles().item(0), operation).then(result -> { + context.setImportedCertificate(new CertificateInfo(result.get())); + return Promise.resolve(flowContext); + })); + + sequential(new FlowContext(Progress.NOOP), tasks) + .timeout(Timeouts.UPLOAD * 1_000) + .then(flowContext -> { + callback.proceed(); + return Promise.resolve(context); + }) + .catch_(failure -> { + if (FlowContext.timeout(failure)) { + wizard().showError(resources.constants().timeout(), resources.messages().operationTimeout(), false); + } else { + wizard().showError(resources.constants().error(), resources.messages().uploadError(item.name), + String.valueOf(failure), false); + } + return Promise.reject(failure); + }); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/installer/certificate.base64 b/app/src/main/java/org/jboss/hal/client/installer/certificate.base64 new file mode 100644 index 0000000000..70740e9d02 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/certificate.base64 @@ -0,0 +1 @@ +bwAAAAMAB2NvbW1lbnRzAHFUaGlzIGZpbGUgaXMgbm90IHVzZWQhIEl0J3MganVzdCBoZXJlIHRvIGdlbmVyYXRlIHRoZSBiYXNlNjQgZW5jb2RlZCB2ZXJzaW9uIHVzaW5nIGh0dHBzOi8vZ2l0aHViLmNvbS9oYWwvZG1yLmNtZAALZGVzY3JpcHRpb25zACJDb21wb25lbnQgY2VydGlmaWNhdGUgaW5mb3JtYXRpb24uAAphdHRyaWJ1dGVzbwAAAAQABmtleS1pZG8AAAADAAR0eXBldHMAC2Rlc2NyaXB0aW9ucwAeVGhlIGhleCBJRCBvZiB0aGUgY2VydGlmaWNhdGUuAAhuaWxsYWJsZVoAAAtmaW5nZXJwcmludG8AAAADAAR0eXBldHMAC2Rlc2NyaXB0aW9ucwAcVGhlIGhhc2ggb2YgdGhlIGNlcnRpZmljYXRlLgAIbmlsbGFibGVaAQALZGVzY3JpcHRpb25vAAAAAwAEdHlwZXRzAAtkZXNjcmlwdGlvbnMALlRoZSBkZXNjcmlwdGlvbiBvZiBpc3N1ZXIgb2YgdGhpcyBjZXJ0aWZpY2F0ZS4ACG5pbGxhYmxlWgEABnN0YXR1c28AAAADAAR0eXBldHMAC2Rlc2NyaXB0aW9ucwA6VGhlIHN0YXR1cyBvZiB0aGUgY2VydGlmaWNhdGUgKFRSVVNURUQsIFJFVk9LRUQsIEVYUElSRUQpLgAIbmlsbGFibGVaAQ== diff --git a/app/src/main/java/org/jboss/hal/client/installer/certificate.dmr b/app/src/main/java/org/jboss/hal/client/installer/certificate.dmr new file mode 100644 index 0000000000..fa02e69771 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/certificate.dmr @@ -0,0 +1,26 @@ +{ + "comment" => "This file is not used! It's just here to generate the base64 encoded version using https://github.com/hal/dmr.cmd", + "description" => "Component certificate information.", + "attributes" => { + "key-id" => { + "type" => STRING, + "description" => "The hex ID of the certificate.", + "nillable" => false + }, + "fingerprint" => { + "type" => STRING, + "description" => "The hash of the certificate.", + "nillable" => true + }, + "description" => { + "type" => STRING, + "description" => "The description of issuer of this certificate.", + "nillable" => true + }, + "status" => { + "type" => STRING, + "description" => "The status of the certificate (TRUSTED, REVOKED, EXPIRED).", + "nillable" => true + } + } +} \ No newline at end of file diff --git a/dmr/src/main/java/org/jboss/hal/dmr/ModelDescriptionConstants.java b/dmr/src/main/java/org/jboss/hal/dmr/ModelDescriptionConstants.java index 2727d55408..a2fd6d14c9 100644 --- a/dmr/src/main/java/org/jboss/hal/dmr/ModelDescriptionConstants.java +++ b/dmr/src/main/java/org/jboss/hal/dmr/ModelDescriptionConstants.java @@ -128,10 +128,15 @@ public interface ModelDescriptionConstants { String CANCEL_NON_PROGRESSING_OPERATION = "cancel-non-progressing-operation"; String CANCEL_OPERATION = "cancel"; String CANCELLED = "cancelled"; + String CERTIFICATES = "certificates"; String CAPABILITY_REFERENCE = "capability-reference"; + String CERT_FILE = "cert-file"; String CERTIFICATE_AUTHORITY = "certificate-authority"; String CERTIFICATE_AUTHORITY_ACCOUNT = "certificate-authority-account"; String CERTIFICATE_DETAILS = "certificate-details"; + String CERTIFICATE_IMPORT = "certificate-import"; + String CERTIFICATE_PARSE = "certificate-parse"; + String CERTIFICATE_REMOVE = "certificate-remove"; String CHAINED_PRINCIPAL_TRANSFORMER = "chained-principal-transformer"; String CHANGE_ACCOUNT_KEY = "change-account-key"; String CHANGE_ALIAS = "change-alias"; @@ -364,6 +369,7 @@ public interface ModelDescriptionConstants { String FILTERING_KEY_STORE = "filtering-key-store"; String FILTERS = "filters"; String FIND_NON_PROGRESSING_OPERATION = "find-non-progressing-operation"; + String FINGERPRINT = "fingerprint"; String FIXED_PORT = "fixed-port"; String FLAG = "flag"; String FLUSH_ALL_CONNECTION_IN_POOL = "flush-all-connection-in-pool"; @@ -499,6 +505,7 @@ public interface ModelDescriptionConstants { String JVM = "jvm"; String KERBEROS_SECURITY_FACTORY = "kerberos-security-factory"; String KEY_ALIAS = "key-alias"; + String KEY_ID = "key-id"; String KEY_MANAGER = "key-manager"; String KEY_OVERRIDES = "key-overrides"; String KEY_SIZE = "key-size"; diff --git a/resources/src/main/java/org/jboss/hal/resources/Constants.java b/resources/src/main/java/org/jboss/hal/resources/Constants.java index 3ed5368c93..dadc8404a3 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Constants.java +++ b/resources/src/main/java/org/jboss/hal/resources/Constants.java @@ -95,6 +95,8 @@ public interface Constants extends com.google.gwt.i18n.client.Constants { String committed(); String completed(); String completedExecutions(); + String updateCertificates(); + String componentCertificateImported(); String composite(); String configuration(); String configurationChanges(); @@ -490,6 +492,7 @@ public interface Constants extends com.google.gwt.i18n.client.Constants { String remoteAddress(); String remove(); String removeAlias(); + String removeComponentCertificate(); String removeContent(); String removeIdentity(); String rename(); diff --git a/resources/src/main/java/org/jboss/hal/resources/Ids.java b/resources/src/main/java/org/jboss/hal/resources/Ids.java index 31ded8b1af..9e1a38f36b 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Ids.java +++ b/resources/src/main/java/org/jboss/hal/resources/Ids.java @@ -858,6 +858,9 @@ public interface Ids { String UPDATE_MANAGER_DOMAIN = "update-manager-domain"; String UPDATE_MANAGER_LIST_UPDATES = "update-manager-list-updates"; String UPDATE_MANAGER_ARTIFACT_CHANGES = "update-manager-artifact-changes"; + String UPDATE_MANAGER_CERTIFICATE = "update-manager-certificate"; + String UPDATE_MANAGER_CERTIFICATE_ADD = "update-manager-certificate-add"; + String UPDATE_MANAGER_CERTIFICATE_REFRESH = "update-manager-certificate-refresh"; String UPDATE_MANAGER_CHANNEL = "update-manager-channel"; String UPDATE_MANAGER_CHANNEL_ADD = "update-manager-channel-add"; String UPDATE_MANAGER_CHANNEL_CHANGES = "update-manager-channel-changes"; diff --git a/resources/src/main/java/org/jboss/hal/resources/Messages.java b/resources/src/main/java/org/jboss/hal/resources/Messages.java index 05c6506760..462548c541 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Messages.java +++ b/resources/src/main/java/org/jboss/hal/resources/Messages.java @@ -545,6 +545,7 @@ public interface Messages extends com.google.gwt.i18n.client.Messages { String changeAccountKeyQuestion(String name); String chooseTemplate(String custom); String cleanPatchHistory(); + SafeHtml componentCertificateImportedDescription(); String configurationChangesDescription(); String configurePatchTitle(); String connectedTo(String url); @@ -591,6 +592,7 @@ public interface Messages extends com.google.gwt.i18n.client.Messages { String identityPasswordSaltedSimpleDigest(); String identityPasswordScramDigest(); String identityPasswordSimpleDigest(); + SafeHtml importComponentCertificateConfirmation(); String invalidFormat(); String invalidJNDIName(); String invalidLength(); @@ -613,6 +615,7 @@ public interface Messages extends com.google.gwt.i18n.client.Messages { String moreThanOneCacheStoreDetails(); String noBootErrors(); String nonEmptyRequires(String fields); + SafeHtml noFileSelected(); String nonProgressingOperation(); String noPatchesForHost(); String notifications(@PluralCount int count); @@ -629,6 +632,7 @@ public interface Messages extends com.google.gwt.i18n.client.Messages { String referenceServer(String server); String reload(String name); String removeConfirmationTitle(String name); + SafeHtml removeUpdateCertificateQuestion(String keyID); String resetConfirmationTitle(String type); String resetStatisticsTitle(); String resourceAdapterColumnFilterDescription(); diff --git a/resources/src/main/java/org/jboss/hal/resources/Names.java b/resources/src/main/java/org/jboss/hal/resources/Names.java index bacfa77bbf..0755a60039 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Names.java +++ b/resources/src/main/java/org/jboss/hal/resources/Names.java @@ -56,6 +56,8 @@ public interface Names { String CACHE_CONTAINER = "Cache Container"; String CACHING_REALM = "Caching Realm"; String CATEGORY = "Category"; + String CERTIFICATE = "Certificate"; + String CERTIFICATES = "Certificates"; String CHANNEL = "Channel"; String CHANNELS = "Channels"; String CLIENT_CONFIGURATION = "Client Configuration"; diff --git a/resources/src/main/java/org/jboss/hal/resources/Previews.java b/resources/src/main/java/org/jboss/hal/resources/Previews.java index f6840444b5..ac49bd98d3 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Previews.java +++ b/resources/src/main/java/org/jboss/hal/resources/Previews.java @@ -365,6 +365,9 @@ public interface Previews extends ClientBundleWithLookup { @Source("previews/update-manager/update-manager.html") ExternalTextResource updateManager(); + @Source("previews/update-manager/certificates.html") + ExternalTextResource updateManagerCertificates(); + @Source("previews/update-manager/channels.html") ExternalTextResource updateManagerChannels(); @@ -399,4 +402,5 @@ public void onSuccess(TextResource textResource) { } } } + } diff --git a/resources/src/main/resources/org/jboss/hal/resources/Constants.properties b/resources/src/main/resources/org/jboss/hal/resources/Constants.properties index ffcd245f92..57d5bbcb9b 100644 --- a/resources/src/main/resources/org/jboss/hal/resources/Constants.properties +++ b/resources/src/main/resources/org/jboss/hal/resources/Constants.properties @@ -93,6 +93,7 @@ commit=Commit committed=Committed completed=Completed completedExecutions=All executions completed successfully +componentCertificateImported=Component certificate imported composite=Composite configuration=Configuration configurationChanges=Configuration Changes @@ -501,6 +502,7 @@ remoteActiveMQServer=Remote ActiveMQ Server remoteAddress=Remote Address remove=Remove removeAlias=Remove Alias +removeComponentCertificate=Remove component certificate removeContent=Remove Content removeIdentity=Remove identity rename=Rename @@ -634,6 +636,7 @@ unsubscribe=Unsubscribe unsubscribeChannel=Unsubscribe channel unsupportedFileType=Unsupported File Type updateAccount=Update Account +updateCertificates=Component certificates updateExistingInstallation=Update existing installation updateManagerHeading=Updating your installation upload=Upload diff --git a/resources/src/main/resources/org/jboss/hal/resources/Messages.properties b/resources/src/main/resources/org/jboss/hal/resources/Messages.properties index 037fd34eb5..92bf5f4570 100644 --- a/resources/src/main/resources/org/jboss/hal/resources/Messages.properties +++ b/resources/src/main/resources/org/jboss/hal/resources/Messages.properties @@ -78,6 +78,7 @@ cloneProfileSuccess=Profile {0} successfully cloned to closeConnectionsSuccess=Connections successfully closed. closeToLogout=In order to logout, please close all currently open browser windows. commitTransactionSuccess=Transaction {0} successfully committed. +componentCertificateImportedDescription=The imported certificate will be used to verify server's components configurationChangesDescription=When configuration changes is enabled, all actions that modify the configurations are recorded, but it is memory only, the records are cleaned when the server or host controller restart. If you want a persistent audit solution use the audit logging. configuredMailServer=Configured mail servers: {0} configurePatchTitle=Configure patch @@ -259,6 +260,7 @@ identityPasswordScramDigest=A password using the SCRAM digest algorithm. identityPasswordSimpleDigest=A simple digest password. importCertificateError=Failed to import the certificate with alias {0} from path {1} for {2}. Cause: {3}. importCertificateSuccess=The certificate with alias {0} was successfully imported from path {1} for resource {2}. +importComponentCertificateConfirmation=Following certificate will be imported and marked as trusted: includeAllHelpText=Configure if all authenticated users should be automatically assigned this role. initKeyManagerError=Failed to initialize Key Manager {0}. Cause: {1}. initKeyManagerSuccess=The Key Manager {0} was successfully initialized. @@ -340,6 +342,7 @@ noDeployment=No deployment specified. Please select a file to upload. noDeploymentPermissions=No deployment permissions found. Please use the button below to create the default deployment permissions. noDeploymentsUploaded=No deployments were uploaded. noExecutions=There are no executions for this job. +noFileSelected=No files were selected for upload. noItems=There are no items for this view. noLogFile=No log file specified. noMacrosDescription=No macros have been recorded so far. To record a macro choose {0} from the tools menu. @@ -456,6 +459,7 @@ removeRunAsRoleError=The role {0} is currently used as run-as r removeSingletonConfirmationQuestion=Do you really want to remove the resource? removeSingletonResourceSuccess={0} successfully removed. removeSingletonSuccess={0} successfully removed. +removeUpdateCertificateQuestion=Are you sure you want to remove component certificate {0} removeUserQuestion=Do you really want to remove user {0}? The user itself won't be removed, instead all assignments of {0} will be removed. removeUserSuccess=All assignments of user {0} successfully removed. replicationColocated=Colocate live and backup server diff --git a/resources/src/main/resources/org/jboss/hal/resources/previews/update-manager/certificates.html b/resources/src/main/resources/org/jboss/hal/resources/previews/update-manager/certificates.html new file mode 100644 index 0000000000..4493587065 --- /dev/null +++ b/resources/src/main/resources/org/jboss/hal/resources/previews/update-manager/certificates.html @@ -0,0 +1,3 @@ +

Lists all the certificates used by the existing JBoss EAP installation to verify its components during updates.
+

You can also add or remove certificates. Once a certificate is added any component signed by this certificate, will +be considered verified.

\ No newline at end of file From 125a48129fe6e5c5753c45d517a5968ce9fe888b Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Mon, 28 Oct 2024 15:49:11 +0000 Subject: [PATCH 2/2] [HAL-1992] Importing missing certificates before update and revert operations --- .../client/installer/CertificateColumn.java | 2 +- .../ConfirmComponentCertificateStep.java | 10 +- .../ImportComponentCertificateWizard.java | 4 +- ...ImportMissingComponentCertificateStep.java | 133 ++++++++++++++++++ .../hal/client/installer/ListUpdatesStep.java | 15 +- .../hal/client/installer/RevertState.java | 2 +- .../hal/client/installer/RevertWizard.java | 30 +++- .../hal/client/installer/UpdateColumn.java | 114 +++++++++++---- .../installer/UpdateManagerContext.java | 22 +++ .../client/installer/UpdateOfflineWizard.java | 4 +- .../client/installer/UpdateOnlineState.java | 2 +- .../client/installer/UpdateOnlineWizard.java | 30 +++- .../client/installer/UpdatePatchWizard.java | 4 +- .../hal/dmr/ModelDescriptionConstants.java | 3 + .../org/jboss/hal/resources/Constants.java | 1 + .../jboss/hal/resources/Constants.properties | 1 + 16 files changed, 330 insertions(+), 47 deletions(-) create mode 100644 app/src/main/java/org/jboss/hal/client/installer/ImportMissingComponentCertificateStep.java diff --git a/app/src/main/java/org/jboss/hal/client/installer/CertificateColumn.java b/app/src/main/java/org/jboss/hal/client/installer/CertificateColumn.java index 08895c819a..a8db8118af 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/CertificateColumn.java +++ b/app/src/main/java/org/jboss/hal/client/installer/CertificateColumn.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 Red Hat + * Copyright 2022 Red Hat * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/app/src/main/java/org/jboss/hal/client/installer/ConfirmComponentCertificateStep.java b/app/src/main/java/org/jboss/hal/client/installer/ConfirmComponentCertificateStep.java index aacab076d7..0ac34df783 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/ConfirmComponentCertificateStep.java +++ b/app/src/main/java/org/jboss/hal/client/installer/ConfirmComponentCertificateStep.java @@ -60,20 +60,20 @@ public ConfirmComponentCertificateStep(final Dispatcher dispatcher, this.statementContext = statementContext; this.resources = resources; - table = new ModelNodeForm.Builder(Ids.build(Ids.UPDATE_MANAGER_LIST_UPDATES), + certificateForm = new ModelNodeForm.Builder(Ids.build(Ids.UPDATE_MANAGER_LIST_UPDATES), Metadata.staticDescription(UpdateManagerResources.INSTANCE.componentCertificate())) .readOnly() .build(); - registerAttachable(table); + registerAttachable(certificateForm); root = div() .add(div().css(marginBottomLarge).innerHtml(resources.messages().importComponentCertificateConfirmation())) - .add(table) + .add(certificateForm) .element(); } private final HTMLElement root; - private final ModelNodeForm table; + private final ModelNodeForm certificateForm; @Override public HTMLElement element() { @@ -82,7 +82,7 @@ public HTMLElement element() { @Override protected void onShow(final ImportComponentCertificateContext context) { - table.view(context.getImportedCertificate()); + certificateForm.view(context.getImportedCertificate()); } @Override diff --git a/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateWizard.java b/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateWizard.java index 79d7e32a13..8af3d7ccc6 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateWizard.java +++ b/app/src/main/java/org/jboss/hal/client/installer/ImportComponentCertificateWizard.java @@ -79,7 +79,9 @@ void show(CertificateColumn column) { }); builder.onFinish((wizard, ctx) -> { - column.refresh(CLEAR_SELECTION); + if (column != null) { + column.refresh(CLEAR_SELECTION); + } wizard.showSuccess(resources.constants().componentCertificateImported(), resources.messages().componentCertificateImportedDescription()); }); diff --git a/app/src/main/java/org/jboss/hal/client/installer/ImportMissingComponentCertificateStep.java b/app/src/main/java/org/jboss/hal/client/installer/ImportMissingComponentCertificateStep.java new file mode 100644 index 0000000000..5de9187cb4 --- /dev/null +++ b/app/src/main/java/org/jboss/hal/client/installer/ImportMissingComponentCertificateStep.java @@ -0,0 +1,133 @@ +/* + * Copyright 2024 Red Hat + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.hal.client.installer; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.hal.ballroom.wizard.AsyncStep; +import org.jboss.hal.ballroom.wizard.WorkflowCallback; +import org.jboss.hal.core.mbui.table.ModelNodeTable; +import org.jboss.hal.dmr.Composite; +import org.jboss.hal.dmr.ModelNode; +import org.jboss.hal.dmr.Operation; +import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.flow.FlowContext; +import org.jboss.hal.flow.Progress; +import org.jboss.hal.flow.Task; +import org.jboss.hal.meta.Metadata; +import org.jboss.hal.meta.StatementContext; +import org.jboss.hal.resources.Ids; +import org.jboss.hal.resources.Resources; +import org.jboss.hal.spi.Message; +import org.jboss.hal.spi.MessageEvent; + +import com.google.web.bindery.event.shared.EventBus; + +import elemental2.dom.HTMLElement; +import elemental2.promise.Promise; + +import static org.jboss.elemento.Elements.div; +import static org.jboss.hal.client.installer.AddressTemplates.INSTALLER_TEMPLATE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERTIFICATE_CONTENT; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERTIFICATE_IMPORT; +import static org.jboss.hal.dmr.ModelDescriptionConstants.DESCRIPTION; +import static org.jboss.hal.dmr.ModelDescriptionConstants.FINGERPRINT; +import static org.jboss.hal.dmr.ModelDescriptionConstants.KEY_ID; +import static org.jboss.hal.flow.Flow.sequential; +import static org.jboss.hal.resources.CSS.marginBottomLarge; + +public class ImportMissingComponentCertificateStep> + extends org.jboss.hal.ballroom.wizard.WizardStep + implements AsyncStep { + private final Dispatcher dispatcher; + private final StatementContext statementContext; + private final Resources resources; + private final ModelNodeTable table; + private final HTMLElement root; + private final EventBus eventBus; + + public ImportMissingComponentCertificateStep(final Dispatcher dispatcher, + final StatementContext statementContext, + final Resources resources, + EventBus eventBus) { + super(resources.constants().missingComponentCertificates()); + + this.dispatcher = dispatcher; + this.statementContext = statementContext; + this.resources = resources; + this.eventBus = eventBus; + + this.table = new ModelNodeTable.Builder(Ids.build(Ids.UPDATE_MANAGER_CERTIFICATE), + Metadata.staticDescription(UpdateManagerResources.INSTANCE.componentCertificate())) + .columns(KEY_ID, DESCRIPTION, FINGERPRINT) + .build(); + registerAttachable(table); + + this.root = div() + .add(div().css(marginBottomLarge).innerHtml(resources.messages().importComponentCertificateConfirmation())) + .add(table) + .element(); + } + + @Override + public HTMLElement element() { + return root; + } + + @Override + protected void onShow(UpdateManagerContext context) { + table.update(context.missingCertInfos); + } + + @Override + public void onNext(final UpdateManagerContext context, final WorkflowCallback callback) { + // import the certificates + Composite importCerts = new Composite(); + + for (String missingCert : context.missingCerts) { + importCerts.add(new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), + CERTIFICATE_IMPORT) + .param(CERTIFICATE_CONTENT, missingCert) + .build()); + } + + List> tasks = List.of( + (flowContext) -> dispatcher.execute(importCerts).then(result -> Promise.resolve(flowContext)), + (flowContext) -> { + return dispatcher.execute(context.listOperation).then( + result -> { + List updates = new ArrayList<>(); + updates.addAll(result.get(context.nodeType).asList()); + context.updates.addAll(updates); + MessageEvent.fire(eventBus, + Message.success(resources.messages().componentCertificateImportedDescription())); + return Promise.resolve(flowContext); + }).catch_((error) -> { + MessageEvent.fire(eventBus, + Message.error(resources.messages().lastOperationFailed(), error.toString())); + return Promise.resolve(flowContext); + }); + }); + + sequential(new FlowContext(Progress.NOOP), tasks) + .then(flowContext -> { + callback.proceed(); + return Promise.resolve(context); + }) + .catch_(Promise::reject); + } +} diff --git a/app/src/main/java/org/jboss/hal/client/installer/ListUpdatesStep.java b/app/src/main/java/org/jboss/hal/client/installer/ListUpdatesStep.java index 76dbf377fe..d92017e36e 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/ListUpdatesStep.java +++ b/app/src/main/java/org/jboss/hal/client/installer/ListUpdatesStep.java @@ -38,10 +38,14 @@ class ListUpdatesStep> extends WizardStep table; + private final String noUpdatesErrorTitle; + private final SafeHtml noUpdatesErrorMsg; ListUpdatesStep(final String title, final SafeHtml tableDescription, - final SafeHtml stepsDescription) { + final SafeHtml stepsDescription, + final String noUpdatesErrorTitle, + final SafeHtml noUpdatesErrorMsg) { super(title); table = new ModelNodeTable.Builder(Ids.build(Ids.UPDATE_MANAGER_LIST_UPDATES), @@ -55,6 +59,9 @@ class ListUpdatesStep> extends WizardStep missingCerts, List missingCertInfos) { + this.eventBus = eventBus; + this.statementContext = statementContext; + this.dispatcher = dispatcher; + this.resources = resources; + this.context = new UpdateManagerContext(updateItem, missingCerts, missingCertInfos, listOperation, ARTIFACT_CHANGES); + } + void show(UpdateColumn column) { Wizard.Builder builder = new Wizard.Builder<>( resources.constants().revertUpdatePreviousState(), context); - builder.stayOpenAfterFinish() + builder.stayOpenAfterFinish(); + + if (context.updates.isEmpty()) { + builder.addStep(IMPORT_CERTIFICATES, new ImportMissingComponentCertificateStep( + dispatcher, statementContext, resources, eventBus)); + } + + builder .addStep(LIST_UPDATES, new ListUpdatesStep( resources.constants().listComponents(), resources.messages().revertComponentsList(), resources.messages().revertComponentsDescription( resources.constants().listComponents(), resources.constants().prepareServerCandidate(), - resources.constants().applyUpdates()))) + resources.constants().applyUpdates()), + resources.constants().noUpdates(), + resources.messages().noUpdatesFound())) .addStep(PREPARE_SERVER, new PrepareStep( context -> new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), PREPARE_REVERT) .param(REVISION, context.updateItem.getName()) - .param(USE_DEFAULT_LOCAL_CACHE, true) .build(), eventBus, dispatcher, statementContext, resources)) .addStep(APPLY_REVERT, new ApplyStep( @@ -82,6 +100,7 @@ void show(UpdateColumn column) { builder.onBack((ctx, currentState) -> { RevertState previous = null; switch (currentState) { + case IMPORT_CERTIFICATES: case LIST_UPDATES: break; case PREPARE_SERVER: @@ -97,6 +116,9 @@ void show(UpdateColumn column) { builder.onNext((ctx, currentState) -> { RevertState next = null; switch (currentState) { + case IMPORT_CERTIFICATES: + next = LIST_UPDATES; + break; case LIST_UPDATES: next = ctx.prepared ? APPLY_REVERT : PREPARE_SERVER; break; diff --git a/app/src/main/java/org/jboss/hal/client/installer/UpdateColumn.java b/app/src/main/java/org/jboss/hal/client/installer/UpdateColumn.java index c562212691..b7255754c5 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/UpdateColumn.java +++ b/app/src/main/java/org/jboss/hal/client/installer/UpdateColumn.java @@ -35,7 +35,9 @@ import org.jboss.hal.dmr.Operation; import org.jboss.hal.dmr.ResourceAddress; import org.jboss.hal.dmr.dispatch.Dispatcher; +import org.jboss.hal.flow.FlowContext; import org.jboss.hal.flow.Progress; +import org.jboss.hal.flow.Task; import org.jboss.hal.meta.MetadataRegistry; import org.jboss.hal.meta.StatementContext; import org.jboss.hal.meta.security.Constraint; @@ -65,7 +67,10 @@ import static org.jboss.hal.client.installer.AddressTemplates.INSTALLER_ADDRESS; import static org.jboss.hal.client.installer.AddressTemplates.INSTALLER_TEMPLATE; import static org.jboss.hal.dmr.ModelDescriptionConstants.ARTIFACT_CHANGES; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERTIFICATE_CONTENT; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CERTIFICATE_INFO; import static org.jboss.hal.dmr.ModelDescriptionConstants.CLEAN; +import static org.jboss.hal.dmr.ModelDescriptionConstants.CONTENT; import static org.jboss.hal.dmr.ModelDescriptionConstants.HISTORY; import static org.jboss.hal.dmr.ModelDescriptionConstants.HISTORY_FROM_REVISION; import static org.jboss.hal.dmr.ModelDescriptionConstants.LIST_UPDATES; @@ -74,8 +79,9 @@ import static org.jboss.hal.dmr.ModelDescriptionConstants.REVISION; import static org.jboss.hal.dmr.ModelDescriptionConstants.TIMESTAMP; import static org.jboss.hal.dmr.ModelDescriptionConstants.TYPE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.UNACCEPTED_CERTIFICATE; import static org.jboss.hal.dmr.ModelDescriptionConstants.UPDATES; -import static org.jboss.hal.dmr.ModelDescriptionConstants.USE_DEFAULT_LOCAL_CACHE; +import static org.jboss.hal.flow.Flow.sequential; import static org.jboss.hal.resources.CSS.fontAwesome; import static org.jboss.hal.resources.CSS.itemText; import static org.jboss.hal.resources.CSS.pfIcon; @@ -220,13 +226,70 @@ public List> actions() { private void updateOnline() { progress.reset(); - Operation operation = new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), LIST_UPDATES) - .param(USE_DEFAULT_LOCAL_CACHE, true) + + // if there are unaccepted licences - open import Wizard (with additional explanation step and no import) + // afterwards go on to check if there are any updates and either show the dialog or update wizard + + Operation listUpdateOp = new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), LIST_UPDATES) .build(); - dispatcher.execute(operation, - result -> { - List updates = result.get(UPDATES).asList(); - if (updates.isEmpty()) { + + prepareUpdateWizard(listUpdateOp, (missingCerts, missingCertInfos, updates) -> { + if (!missingCerts.isEmpty()) { + new UpdateOnlineWizard(eventBus, dispatcher, statementContext, resources, listUpdateOp, missingCerts, + missingCertInfos).show(this); + } else { + new UpdateOnlineWizard(eventBus, dispatcher, statementContext, resources, updates).show(this); + } + }, UPDATES); + + progress.finish(); + + } + + private void prepareUpdateWizard(Operation listUpdateOp, WizardBuilder wizardBuilder, String updateType) { + Operation licenseCheckOp = new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), + UNACCEPTED_CERTIFICATE) + .build(); + + List updates = new ArrayList<>(); + List missingCerts = new ArrayList<>(); + List missingCertInfos = new ArrayList<>(); + List> tasks = List.of( + (flowContext) -> { + return dispatcher.execute(licenseCheckOp).then( + result -> { + for (ModelNode node : result.asList()) { + missingCerts.add(node.get(CERTIFICATE_CONTENT).asString()); + missingCertInfos.add(new CertificateInfo(node.get(CERTIFICATE_INFO))); + } + return Promise.resolve(flowContext); + }).catch_((error) -> { + progress.finish(); + MessageEvent.fire(eventBus, + Message.error(resources.messages().lastOperationFailed(), error.toString())); + return Promise.resolve(flowContext); + }); + }, + (flowContext) -> { + // get the list of updates if no certificates are missing + if (missingCerts.isEmpty()) { + return dispatcher.execute(listUpdateOp).then( + result -> { + updates.addAll(result.get(updateType).asList()); + return Promise.resolve(flowContext); + }).catch_((error) -> { + progress.finish(); + MessageEvent.fire(eventBus, + Message.error(resources.messages().lastOperationFailed(), error.toString())); + return Promise.resolve(flowContext); + }); + } else { + return Promise.resolve(flowContext); + } + }, + (flowContext) -> { + // build and show the wizard + if (missingCerts.isEmpty() && updates.isEmpty()) { Dialog dialog = new Dialog.Builder(resources.constants().noUpdates()) .add(p().innerHtml(resources.messages().noUpdatesFound()).element()) .closeOnEsc(true) @@ -235,13 +298,14 @@ private void updateOnline() { .build(); dialog.show(); } else { - new UpdateOnlineWizard(eventBus, dispatcher, statementContext, resources, updates).show(this); + wizardBuilder.build(missingCerts, missingCertInfos, updates); } progress.finish(); - }, (op, error) -> { - progress.finish(); - MessageEvent.fire(eventBus, Message.error(resources.messages().lastOperationFailed())); + return Promise.resolve(flowContext); }); + + sequential(new FlowContext(Progress.NOOP), tasks) + .catch_(Promise::reject); } private void updateOffline() { @@ -263,20 +327,18 @@ private void revert(UpdateItem updateItem) { HISTORY_FROM_REVISION) .param(REVISION, updateItem.getName()) .build(); - dispatcher.execute(operation, - result -> { - List updates = result.get(ARTIFACT_CHANGES).asList(); - if (updates.isEmpty()) { - Dialog dialog = new Dialog.Builder(resources.constants().noUpdates()) - .add(p().innerHtml(resources.messages().noUpdatesFound()).element()) - .closeOnEsc(true) - .closeOnly() - .size(Dialog.Size.SMALL) - .build(); - dialog.show(); - } else { - new RevertWizard(eventBus, dispatcher, statementContext, resources, updateItem, updates).show(this); - } - }, (op, error) -> MessageEvent.fire(eventBus, Message.error(resources.messages().lastOperationFailed()))); + + prepareUpdateWizard(operation, (missingCerts, missingCertInfos, updates) -> { + if (!missingCerts.isEmpty()) { + new RevertWizard(eventBus, dispatcher, statementContext, resources, updateItem, operation, missingCerts, + missingCertInfos).show(this); + } else { + new RevertWizard(eventBus, dispatcher, statementContext, resources, updateItem, updates).show(this); + } + }, ARTIFACT_CHANGES); + } + + interface WizardBuilder { + void build(List missingCerts, List missingCertInfos, List updates); } } diff --git a/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerContext.java b/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerContext.java index ea27b2bee0..e510b20caa 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerContext.java +++ b/app/src/main/java/org/jboss/hal/client/installer/UpdateManagerContext.java @@ -15,9 +15,11 @@ */ package org.jboss.hal.client.installer; +import java.util.ArrayList; import java.util.List; import org.jboss.hal.dmr.ModelNode; +import org.jboss.hal.dmr.Operation; import static java.util.Collections.emptyList; @@ -28,6 +30,10 @@ class UpdateManagerContext { UpdateItem updateItem; List updates; String workDir; + List missingCerts; + List missingCertInfos; + Operation listOperation; + String nodeType; UpdateManagerContext() { this(emptyList(), null); @@ -41,4 +47,20 @@ class UpdateManagerContext { this.updateItem = updateItem; this.updates = updates; } + + public UpdateManagerContext(List missingCerts, List missingCertInfos, + Operation listOperation, String nodeType) { + this.missingCerts = missingCerts; + this.missingCertInfos = missingCertInfos; + this.listOperation = listOperation; + this.nodeType = nodeType; + // updates list will be modified later - don't use emptyList() + this.updates = new ArrayList<>(); + } + + public UpdateManagerContext(UpdateItem updateItem, List missingCerts, List missingCertInfos, + Operation listOperation, String nodeType) { + this(missingCerts, missingCertInfos, listOperation, nodeType); + this.updateItem = updateItem; + } } diff --git a/app/src/main/java/org/jboss/hal/client/installer/UpdateOfflineWizard.java b/app/src/main/java/org/jboss/hal/client/installer/UpdateOfflineWizard.java index 0ec296f142..06eebc5fea 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/UpdateOfflineWizard.java +++ b/app/src/main/java/org/jboss/hal/client/installer/UpdateOfflineWizard.java @@ -67,7 +67,9 @@ void show(UpdateColumn column) { resources.messages().updateInstallationDescription( resources.constants().listComponents(), resources.constants().prepareServerCandidate(), - resources.constants().applyUpdates()))) + resources.constants().applyUpdates()), + resources.constants().noUpdates(), + resources.messages().noUpdatesFound())) .addStep(PREPARE_SERVER, new PrepareStep( updateManagerContext -> new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), PREPARE_UPDATES) diff --git a/app/src/main/java/org/jboss/hal/client/installer/UpdateOnlineState.java b/app/src/main/java/org/jboss/hal/client/installer/UpdateOnlineState.java index 5d95614bbd..ffa05b3c4c 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/UpdateOnlineState.java +++ b/app/src/main/java/org/jboss/hal/client/installer/UpdateOnlineState.java @@ -17,5 +17,5 @@ enum UpdateOnlineState { - LIST_UPDATES, PREPARE_SERVER, APPLY_UPDATE + LIST_UPDATES, PREPARE_SERVER, APPLY_UPDATE, IMPORT_CERTIFICATES } diff --git a/app/src/main/java/org/jboss/hal/client/installer/UpdateOnlineWizard.java b/app/src/main/java/org/jboss/hal/client/installer/UpdateOnlineWizard.java index 1bae3eb8ae..f08ed8a07c 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/UpdateOnlineWizard.java +++ b/app/src/main/java/org/jboss/hal/client/installer/UpdateOnlineWizard.java @@ -28,11 +28,12 @@ import static org.jboss.hal.client.installer.AddressTemplates.INSTALLER_TEMPLATE; import static org.jboss.hal.client.installer.UpdateOnlineState.APPLY_UPDATE; +import static org.jboss.hal.client.installer.UpdateOnlineState.IMPORT_CERTIFICATES; import static org.jboss.hal.client.installer.UpdateOnlineState.LIST_UPDATES; import static org.jboss.hal.client.installer.UpdateOnlineState.PREPARE_SERVER; import static org.jboss.hal.core.finder.FinderColumn.RefreshMode.CLEAR_SELECTION; import static org.jboss.hal.dmr.ModelDescriptionConstants.PREPARE_UPDATES; -import static org.jboss.hal.dmr.ModelDescriptionConstants.USE_DEFAULT_LOCAL_CACHE; +import static org.jboss.hal.dmr.ModelDescriptionConstants.UPDATES; class UpdateOnlineWizard { @@ -51,21 +52,38 @@ class UpdateOnlineWizard { this.context = new UpdateManagerContext(updates); } + public UpdateOnlineWizard(EventBus eventBus, Dispatcher dispatcher, StatementContext statementContext, Resources resources, + Operation listOperation, List missingCerts, List missingCertInfos) { + this.eventBus = eventBus; + this.statementContext = statementContext; + this.dispatcher = dispatcher; + this.resources = resources; + this.context = new UpdateManagerContext(missingCerts, missingCertInfos, listOperation, UPDATES); + } + void show(UpdateColumn column) { Wizard.Builder builder = new Wizard.Builder<>( resources.constants().updateExistingInstallation(), context); - builder.stayOpenAfterFinish() + builder.stayOpenAfterFinish(); + + if (context.updates.isEmpty()) { + builder.addStep(IMPORT_CERTIFICATES, new ImportMissingComponentCertificateStep( + dispatcher, statementContext, resources, eventBus)); + } + + builder .addStep(LIST_UPDATES, new ListUpdatesStep( resources.constants().listUpdates(), resources.messages().availableComponentsList(), resources.messages().updateInstallationDescription( resources.constants().listComponents(), resources.constants().prepareServerCandidate(), - resources.constants().applyUpdates()))) + resources.constants().applyUpdates()), + resources.constants().noUpdates(), + resources.messages().noUpdatesFound())) .addStep(PREPARE_SERVER, new PrepareStep( (__) -> new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), PREPARE_UPDATES) - .param(USE_DEFAULT_LOCAL_CACHE, true) .build(), eventBus, dispatcher, statementContext, resources)) .addStep(APPLY_UPDATE, new ApplyStep( @@ -82,6 +100,7 @@ void show(UpdateColumn column) { builder.onBack((ctx, currentState) -> { UpdateOnlineState previous = null; switch (currentState) { + case IMPORT_CERTIFICATES: case LIST_UPDATES: break; case PREPARE_SERVER: @@ -97,6 +116,9 @@ void show(UpdateColumn column) { builder.onNext((ctx, currentState) -> { UpdateOnlineState next = null; switch (currentState) { + case IMPORT_CERTIFICATES: + next = LIST_UPDATES; + break; case LIST_UPDATES: next = ctx.prepared ? APPLY_UPDATE : PREPARE_SERVER; break; diff --git a/app/src/main/java/org/jboss/hal/client/installer/UpdatePatchWizard.java b/app/src/main/java/org/jboss/hal/client/installer/UpdatePatchWizard.java index 7129ade049..55c13aa091 100644 --- a/app/src/main/java/org/jboss/hal/client/installer/UpdatePatchWizard.java +++ b/app/src/main/java/org/jboss/hal/client/installer/UpdatePatchWizard.java @@ -70,7 +70,9 @@ void show(UpdateColumn column) { resources.messages().updateInstallationDescription( resources.constants().listComponents(), resources.constants().prepareServerCandidate(), - resources.constants().applyUpdates()))) + resources.constants().applyUpdates()), + resources.constants().noUpdates(), + resources.messages().noUpdatesFound())) .addStep(PREPARE_SERVER, new PrepareStep( (__) -> new Operation.Builder(INSTALLER_TEMPLATE.resolve(statementContext), PREPARE_UPDATES).build(), eventBus, dispatcher, statementContext, resources)) diff --git a/dmr/src/main/java/org/jboss/hal/dmr/ModelDescriptionConstants.java b/dmr/src/main/java/org/jboss/hal/dmr/ModelDescriptionConstants.java index a2fd6d14c9..2dea3151d3 100644 --- a/dmr/src/main/java/org/jboss/hal/dmr/ModelDescriptionConstants.java +++ b/dmr/src/main/java/org/jboss/hal/dmr/ModelDescriptionConstants.java @@ -133,8 +133,10 @@ public interface ModelDescriptionConstants { String CERT_FILE = "cert-file"; String CERTIFICATE_AUTHORITY = "certificate-authority"; String CERTIFICATE_AUTHORITY_ACCOUNT = "certificate-authority-account"; + String CERTIFICATE_CONTENT = "certificate-content"; String CERTIFICATE_DETAILS = "certificate-details"; String CERTIFICATE_IMPORT = "certificate-import"; + String CERTIFICATE_INFO = "certificate-info"; String CERTIFICATE_PARSE = "certificate-parse"; String CERTIFICATE_REMOVE = "certificate-remove"; String CHAINED_PRINCIPAL_TRANSFORMER = "chained-principal-transformer"; @@ -1041,6 +1043,7 @@ public interface ModelDescriptionConstants { String TRUST_MANAGER = "trust-manager"; String TRUST_MODULE = "trust-module"; String TYPE = "type"; + String UNACCEPTED_CERTIFICATE="unaccepted-certificates"; String UNDEFINE_ATTRIBUTE_OPERATION = "undefine-attribute"; String UNDEFINED = "undefined"; String UNDEPLOY = "undeploy"; diff --git a/resources/src/main/java/org/jboss/hal/resources/Constants.java b/resources/src/main/java/org/jboss/hal/resources/Constants.java index dadc8404a3..79bd44814a 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Constants.java +++ b/resources/src/main/java/org/jboss/hal/resources/Constants.java @@ -384,6 +384,7 @@ public interface Constants extends com.google.gwt.i18n.client.Constants { String minute(); String minutes(); String missCount(); + String missingComponentCertificates(); String modelBrowser(); String monitor(); String move(); diff --git a/resources/src/main/resources/org/jboss/hal/resources/Constants.properties b/resources/src/main/resources/org/jboss/hal/resources/Constants.properties index 57d5bbcb9b..4865503cf2 100644 --- a/resources/src/main/resources/org/jboss/hal/resources/Constants.properties +++ b/resources/src/main/resources/org/jboss/hal/resources/Constants.properties @@ -391,6 +391,7 @@ minimum=Minimum minute=minute minutes=minutes missCount=Miss Count +missingComponentCertificates=Import missing certificates modelBrowser=Management Model monitor=Monitor move=Move