diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java index 805b66d75..1e58ea938 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java @@ -54,6 +54,7 @@ import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetInfo; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.specifications.TargetSpecifications; +import org.eclipse.hawkbit.security.SystemSecurityContext; import org.hibernate.validator.constraints.NotEmpty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -118,6 +119,9 @@ public class DeploymentManagement { @Autowired private AfterTransactionCommitExecutor afterCommit; + @Autowired + private SystemSecurityContext systemSecurityContext; + /** * method assigns the {@link DistributionSet} to all {@link Target}s. * @@ -422,11 +426,14 @@ public class DeploymentManagement { private void assignDistributionSetEvent(final Target target, final Long actionId, final List softwareModules) { target.getTargetInfo().setUpdateStatus(TargetUpdateStatus.PENDING); + final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> { + return target.getSecurityToken(); + }); afterCommit.afterCommit(() -> { eventBus.post(new TargetInfoUpdateEvent(target.getTargetInfo())); eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(), target.getControllerId(), actionId, softwareModules, target.getTargetInfo().getAddress(), - target.getSecurityToken())); + targetSecurityToken)); }); } diff --git a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Target.java b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Target.java index 5f37fb653..8947ea6c3 100644 --- a/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Target.java +++ b/hawkbit-repository/src/main/java/org/eclipse/hawkbit/repository/model/Target.java @@ -38,6 +38,7 @@ import javax.validation.constraints.Size; import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.repository.model.helper.SecurityChecker; import org.eclipse.hawkbit.repository.model.helper.SecurityTokenGeneratorHolder; +import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder; import org.eclipse.persistence.annotations.CascadeOnDelete; import org.springframework.data.domain.Persistable; @@ -193,10 +194,14 @@ public class Target extends NamedEntity implements Persistable { } /** - * @return the securityToken + * @return the securityToken if the current security context contains the + * necessary permission {@link SpPermission#READ_TARGET_SEC_TOKEN} + * or the current context is executed as system code, otherwise + * {@code null}. */ public String getSecurityToken() { - if (SecurityChecker.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) { + if (SystemSecurityContextHolder.getInstance().getSystemSecurityContext().isCurrentThreadSystemCode() + || SecurityChecker.hasPermission(SpPermission.READ_TARGET_SEC_TOKEN)) { return securityToken; } return null; diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/AbstractIntegrationTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/AbstractIntegrationTest.java index b7091edf8..ddd850df1 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/AbstractIntegrationTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/AbstractIntegrationTest.java @@ -48,6 +48,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.utils.RepositoryDataGenerator.DatabaseCleanupUtil; import org.eclipse.hawkbit.security.DosFilter; +import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; import org.junit.After; import org.junit.AfterClass; @@ -181,11 +182,10 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware { @Autowired protected TenantAwareCacheManager cacheManager; - + @Autowired protected TenantConfigurationManagement tenantConfigurationManagement; - @Autowired protected RolloutManagement rolloutManagement; @@ -198,6 +198,9 @@ public abstract class AbstractIntegrationTest implements EnvironmentAware { @Autowired protected RolloutRepository rolloutRepository; + @Autowired + protected SystemSecurityContext systemSecurityContext; + protected MockMvc mvc; @Autowired diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/WithSpringAuthorityRule.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/WithSpringAuthorityRule.java index 15fda51b4..40f6a64c2 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/WithSpringAuthorityRule.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/WithSpringAuthorityRule.java @@ -160,19 +160,23 @@ public class WithSpringAuthorityRule implements TestRule { } public static WithUser withUser(final String principal, final String... authorities) { - return withUserAndTenant(principal, "default", true, authorities); + return withUserAndTenant(principal, "default", true, true, authorities); + } + + public static WithUser withUser(final String principal, final boolean allSpPermision, final String... authorities) { + return withUserAndTenant(principal, "default", true, allSpPermision, authorities); } public static WithUser withUser(final boolean autoCreateTenant) { - return withUserAndTenant("bumlux", "default", autoCreateTenant, new String[] {}); + return withUserAndTenant("bumlux", "default", autoCreateTenant, true, new String[] {}); } public static WithUser withUserAndTenant(final String principal, final String tenant, final String... authorities) { - return withUserAndTenant(principal, tenant, true, new String[] {}); + return withUserAndTenant(principal, tenant, true, true, new String[] {}); } public static WithUser withUserAndTenant(final String principal, final String tenant, - final boolean autoCreateTenant, final String... authorities) { + final boolean autoCreateTenant, final boolean allSpPermission, final String... authorities) { return new WithUser() { @Override @@ -197,7 +201,7 @@ public class WithSpringAuthorityRule implements TestRule { @Override public boolean allSpPermissions() { - return true; + return allSpPermission; } @Override diff --git a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementTest.java b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementTest.java index a223c25b4..1c039c8c8 100644 --- a/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementTest.java +++ b/hawkbit-repository/src/test/java/org/eclipse/hawkbit/repository/TargetManagementTest.java @@ -32,6 +32,7 @@ import org.eclipse.hawkbit.AbstractIntegrationTest; import org.eclipse.hawkbit.TestDataUtil; import org.eclipse.hawkbit.WithSpringAuthorityRule; import org.eclipse.hawkbit.WithUser; +import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.model.Action; @@ -56,6 +57,36 @@ import ru.yandex.qatools.allure.annotations.Stories; @Stories("Target Management") public class TargetManagementTest extends AbstractIntegrationTest { + @Test + @Description("Ensures that retrieving the target security is only permitted with the necessary permissions.") + public void getTargetSecurityTokenOnlyWithCorrectPermission() throws Exception { + final Target createdTarget = targetManagement.createTarget(new Target("targetWithSecurityToken")); + + // retrieve security token only with READ_TARGET_SEC_TOKEN permission + final String securityTokenWithReadPermission = securityRule.runAs(WithSpringAuthorityRule + .withUser("OnlyTargetReadPermission", false, SpPermission.READ_TARGET_SEC_TOKEN.toString()), () -> { + return createdTarget.getSecurityToken(); + }); + + // retrieve security token as system code execution + final String securityTokenAsSystemCode = systemSecurityContext.runAsSystem(() -> { + return createdTarget.getSecurityToken(); + }); + + // retrieve security token without any permissions + final String securityTokenWithoutPermission = securityRule + .runAs(WithSpringAuthorityRule.withUser("NoPermission", false), () -> { + return createdTarget.getSecurityToken(); + }); + + assertThat(createdTarget.getSecurityToken()).isNotNull(); + assertThat(securityTokenWithReadPermission).isNotNull(); + assertThat(securityTokenAsSystemCode).isNotNull(); + + assertThat(securityTokenWithoutPermission).isNull(); + + } + @Test @Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists.") @WithUser(tenantId = "tenantWhichDoesNotExists", allSpPermissions = true, autoCreateTenant = false) diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SystemSecurityContext.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SystemSecurityContext.java index b22b54e39..f849eb541 100644 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SystemSecurityContext.java +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SystemSecurityContext.java @@ -49,6 +49,21 @@ public class SystemSecurityContext { this.tenantAware = tenantAware; } + /** + * Runs a given {@link Callable} within a system security context, which is + * permitted to call secured system code. Often the system needs to call + * secured methods by it's own without relying on the current security + * context e.g. if the current security context does not contain the + * necessary permission it's necessary to execute code as system code to + * execute necessary methods and functionality. + * + * The security context will be switched to the system code and back after + * the callable is called. + * + * @param callable + * the callable to call within the system security context + * @return the return value of the {@link Callable#call()} method. + */ public T runAsSystem(final Callable callable) { final SecurityContext oldContext = SecurityContextHolder.getContext(); try { @@ -68,6 +83,14 @@ public class SystemSecurityContext { } } + /** + * @return {@code true} if the current running code is running as system + * code block. + */ + public boolean isCurrentThreadSystemCode() { + return SecurityContextHolder.getContext().getAuthentication() instanceof SystemCodeAuthentication; + } + private static void setSystemContext() { final SecurityContextImpl securityContextImpl = new SecurityContextImpl(); securityContextImpl.setAuthentication(new SystemCodeAuthentication()); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java index b41f3dfe9..6a671e228 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/CreateOrUpdateFilterHeader.java @@ -68,6 +68,8 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button private static final long serialVersionUID = 7474232427119031474L; + private static final String breadcrumbCustomFilters = "breadcrumb.target.filter.custom.filters"; + @Autowired private I18N i18n; @@ -93,6 +95,12 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button @Qualifier("uiExecutor") private transient Executor executor; + private HorizontalLayout breadcrumbLayout; + + private Button breadcrumbButton; + + private Label breadcrumbName; + private Label headerCaption; private TextField queryTextField; @@ -169,6 +177,7 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button oldFilterName = filterManagementUIState.getTfQuery().get().getName(); oldFilterQuery = filterManagementUIState.getTfQuery().get().getQuery(); } + breadcrumbName.setValue(nameLabel.getValue()); showValidationSuccesIcon(); titleFilterIconsLayout.addStyleName(SPUIStyleDefinitions.TARGET_FILTER_CAPTION_LAYOUT); headerCaption.setVisible(false); @@ -177,6 +186,7 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button private void resetComponents() { headerCaption.setVisible(true); + breadcrumbName.setValue(headerCaption.getValue()); nameLabel.setValue(""); queryTextField.setValue(""); setInitialStatusIconStyle(validationIcon); @@ -201,6 +211,9 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button } private void createComponents() { + + breadcrumbButton = createBreadcrumbButton(); + headerCaption = SPUIComponentProvider.getLabel(SPUILabelDefinitions.VAR_CREATE_FILTER, SPUILabelDefinitions.SP_WIDGET_CAPTION); @@ -221,12 +234,23 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button closeIcon = createSearchResetIcon(); } + private Button createBreadcrumbButton() { + final Button createFilterViewLink = SPUIComponentProvider.getButton(null, "", "", null, false, null, + SPUIButtonStyleSmallNoBorder.class); + createFilterViewLink.setStyleName(ValoTheme.LINK_SMALL + " " + "on-focus-no-border link rollout-caption-links"); + createFilterViewLink.setDescription(i18n.get(breadcrumbCustomFilters)); + createFilterViewLink.setCaption(i18n.get(breadcrumbCustomFilters)); + createFilterViewLink.addClickListener(value -> showCustomFiltersView()); + + return createFilterViewLink; + } + private TextField createNameTextField() { final TextField nameField = SPUIComponentProvider.getTextField("", ValoTheme.TEXTFIELD_TINY, false, null, i18n.get("textfield.customfiltername"), true, SPUILabelDefinitions.TEXT_FIELD_MAX_LENGTH); nameField.setId(SPUIComponetIdProvider.CUSTOM_FILTER_ADD_NAME); nameField.setPropertyDataSource(nameLabel); - nameField.addTextChangeListener(event -> onFiterNameChange(event)); + nameField.addTextChangeListener(event -> onFilterNameChange(event)); return nameField; } @@ -256,7 +280,7 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button }; } - private void onFiterNameChange(final TextChangeEvent event) { + private void onFilterNameChange(final TextChangeEvent event) { if (isNameAndQueryEmpty(event.getText(), queryTextField.getValue()) || (event.getText().equals(oldFilterName) && queryTextField.getValue().equals(oldFilterQuery))) { saveButton.setEnabled(false); @@ -276,6 +300,13 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button titleFilterIconsLayout.addComponents(headerCaption, captionLayout); titleFilterIconsLayout.setSpacing(true); + breadcrumbLayout = new HorizontalLayout(); + breadcrumbLayout.addComponent(breadcrumbButton); + breadcrumbLayout.addComponent(new Label(">")); + breadcrumbName = SPUIComponentProvider.getLabel(null, SPUILabelDefinitions.SP_WIDGET_CAPTION); + breadcrumbLayout.addComponent(breadcrumbName); + breadcrumbName.addStyleName("breadcrumbPaddingLeft"); + final HorizontalLayout titleFilterLayout = new HorizontalLayout(); titleFilterLayout.setSizeFull(); titleFilterLayout.addComponents(titleFilterIconsLayout, closeIcon); @@ -302,10 +333,12 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button queryLayout.setSpacing(true); queryLayout.addComponents(searchLayout, iconLayout); + addComponent(breadcrumbLayout); addComponent(titleFilterLayout); addComponent(queryLayout); setSpacing(true); addStyleName(SPUIStyleDefinitions.WIDGET_TITLE); + addStyleName("bordered-layout"); } private void setUpCaptionLayout(final boolean isCreateView) { @@ -524,4 +557,8 @@ public class CreateOrUpdateFilterHeader extends VerticalLayout implements Button } } + private void showCustomFiltersView() { + eventBus.publish(this, CustomFilterUIEvent.SHOW_FILTER_MANAGEMENT); + } + } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java index 06cbe4d69..ef759aa5f 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/FilterManagementView.java @@ -96,7 +96,8 @@ public class FilterManagementView extends VerticalLayout implements View { } else if (custFilterUIEvent == CustomFilterUIEvent.CREATE_NEW_FILTER_CLICK || custFilterUIEvent == CustomFilterUIEvent.FILTER_TARGET_BY_QUERY) { this.getUI().access(() -> viewCreateTargetFilterLayout()); - } else if (custFilterUIEvent == CustomFilterUIEvent.EXIT_CREATE_OR_UPDATE_FILTRER_VIEW) { + } else if (custFilterUIEvent == CustomFilterUIEvent.EXIT_CREATE_OR_UPDATE_FILTRER_VIEW + || custFilterUIEvent == CustomFilterUIEvent.SHOW_FILTER_MANAGEMENT) { UI.getCurrent().access(() -> viewListView()); } } @@ -121,11 +122,11 @@ public class FilterManagementView extends VerticalLayout implements View { tableHeaderLayout.setComponentAlignment(createNewFilterHeader, Alignment.TOP_CENTER); tableHeaderLayout.addComponent(createNewFilterTable); tableHeaderLayout.setComponentAlignment(createNewFilterTable, Alignment.TOP_CENTER); - tableHeaderLayout.setExpandRatio(createNewFilterTable, 1.0f); + tableHeaderLayout.setExpandRatio(createNewFilterTable, 1.0F); addComponent(tableHeaderLayout); setComponentAlignment(tableHeaderLayout, Alignment.TOP_CENTER); - setExpandRatio(tableHeaderLayout, 1.0f); + setExpandRatio(tableHeaderLayout, 1.0F); final HorizontalLayout targetsCountmessageLabelLayout = addTargetFilterMessageLabel(); addComponent(targetsCountmessageLabelLayout); @@ -135,17 +136,17 @@ public class FilterManagementView extends VerticalLayout implements View { private void viewListView() { removeAllComponents(); - final VerticalLayout tableHeaderLayout = new VerticalLayout(); - tableHeaderLayout.setSizeFull(); - tableHeaderLayout.setSpacing(false); - tableHeaderLayout.setMargin(false); - tableHeaderLayout.setStyleName("table-layout"); - tableHeaderLayout.addComponent(targetFilterHeader); - tableHeaderLayout.setComponentAlignment(targetFilterHeader, Alignment.TOP_CENTER); - tableHeaderLayout.addComponent(targetFilterTable); - tableHeaderLayout.setComponentAlignment(targetFilterTable, Alignment.TOP_CENTER); - tableHeaderLayout.setExpandRatio(targetFilterTable, 1.0f); - addComponent(tableHeaderLayout); + final VerticalLayout tableListViewLayout = new VerticalLayout(); + tableListViewLayout.setSizeFull(); + tableListViewLayout.setSpacing(false); + tableListViewLayout.setMargin(false); + tableListViewLayout.setStyleName("table-layout"); + tableListViewLayout.addComponent(targetFilterHeader); + tableListViewLayout.setComponentAlignment(targetFilterHeader, Alignment.TOP_CENTER); + tableListViewLayout.addComponent(targetFilterTable); + tableListViewLayout.setComponentAlignment(targetFilterTable, Alignment.TOP_CENTER); + tableListViewLayout.setExpandRatio(targetFilterTable, 1.0F); + addComponent(tableListViewLayout); } private HorizontalLayout addTargetFilterMessageLabel() { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/event/CustomFilterUIEvent.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/event/CustomFilterUIEvent.java index 57de12aca..a09d3d397 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/event/CustomFilterUIEvent.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/event/CustomFilterUIEvent.java @@ -15,5 +15,5 @@ package org.eclipse.hawkbit.ui.filtermanagement.event; * */ public enum CustomFilterUIEvent { - FILTER_TARGET_BY_QUERY, FILTER_BY_CUST_FILTER_TEXT, FILTER_BY_CUST_FILTER_TEXT_REMOVE, CREATE_NEW_FILTER_CLICK, EXIT_CREATE_OR_UPDATE_FILTRER_VIEW, TARGET_FILTER_DETAIL_VIEW, TARGET_DETAILS_VIEW, CREATE_TARGET_FILTER_QUERY, UPDATED_TARGET_FILTER_QUERY, UPDATE_TARGET_FILTER_SEARCH_ICON + FILTER_TARGET_BY_QUERY, FILTER_BY_CUST_FILTER_TEXT, FILTER_BY_CUST_FILTER_TEXT_REMOVE, CREATE_NEW_FILTER_CLICK, EXIT_CREATE_OR_UPDATE_FILTRER_VIEW, TARGET_FILTER_DETAIL_VIEW, TARGET_DETAILS_VIEW, CREATE_TARGET_FILTER_QUERY, UPDATED_TARGET_FILTER_QUERY, UPDATE_TARGET_FILTER_SEARCH_ICON, SHOW_FILTER_MANAGEMENT } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/login/LoginView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/login/LoginView.java index 0ddf0fcd3..a6cc46708 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/login/LoginView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/login/LoginView.java @@ -225,7 +225,7 @@ public class LoginView extends VerticalLayout implements View { final String linkStyle = "v-link"; if (!uiProperties.getLinks().getDocumentation().getRoot().isEmpty()) { - final Link docuLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_DOCUMENATION, + final Link docuLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_DOCUMENTATION, i18n.get("link.documentation.name"), uiProperties.getLinks().getDocumentation().getRoot(), FontAwesome.QUESTION_CIRCLE, "_blank", linkStyle, true); links.addComponent(docuLink); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java index 0db3ac566..8699f8a87 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/menu/DashboardMenu.java @@ -148,7 +148,7 @@ public final class DashboardMenu extends CustomComponent { final String linkStyle = "v-link"; if (!uiProperties.getLinks().getDocumentation().getRoot().isEmpty()) { - final Link docuLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_DOCUMENATION, + final Link docuLink = SPUIComponentProvider.getLink(SPUIComponetIdProvider.LINK_DOCUMENTATION, i18n.get("link.documentation.name"), uiProperties.getLinks().getDocumentation().getRoot(), FontAwesome.QUESTION_CIRCLE, "_blank", linkStyle, true); docuLink.setDescription(i18n.get("link.documentation.name")); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgroup/RolloutGroupsListHeader.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgroup/RolloutGroupsListHeader.java index 45c16eeed..e85d96d7a 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgroup/RolloutGroupsListHeader.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgroup/RolloutGroupsListHeader.java @@ -161,6 +161,7 @@ public class RolloutGroupsListHeader extends AbstractGridHeader { final HorizontalLayout headerCaptionLayout = new HorizontalLayout(); headerCaptionLayout.addComponent(rolloutsListViewLink); headerCaptionLayout.addComponent(new Label(">")); + headerCaption.addStyleName("breadcrumbPaddingLeft"); headerCaptionLayout.addComponent(headerCaption); return headerCaptionLayout; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIComponetIdProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIComponetIdProvider.java index 48e501e1d..91f54c9c4 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIComponetIdProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUIComponetIdProvider.java @@ -599,7 +599,7 @@ public final class SPUIComponetIdProvider { /** * Documentation Link in Login view and menu. */ - public static final String LINK_DOCUMENATION = "link.documentation"; + public static final String LINK_DOCUMENTATION = "link.documentation"; /** * Demo Link in Login view and menu. diff --git a/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/others.scss b/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/others.scss index 879fb8dd6..d327baa94 100644 --- a/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/others.scss +++ b/hawkbit-ui/src/main/resources/VAADIN/themes/hawkbit/customstyles/others.scss @@ -228,4 +228,8 @@ .v-tooltip{ max-width:43em; } + + .breadcrumbPaddingLeft{ + padding-left: 3px !important; + } } diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 25dabd354..0f65ebe13 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -487,3 +487,6 @@ message.error.starting.rollout = Server error. Error starting rollout. Please co #Menu menu.title = Software Provisioning + +#Target Filter Management +breadcrumb.target.filter.custom.filters = Custom Filters diff --git a/hawkbit-ui/src/main/resources/messages_de.properties b/hawkbit-ui/src/main/resources/messages_de.properties index 39661e3c9..12f04048a 100644 --- a/hawkbit-ui/src/main/resources/messages_de.properties +++ b/hawkbit-ui/src/main/resources/messages_de.properties @@ -473,3 +473,6 @@ label.target.per.group = Targets per group : message.dist.already.assigned = Distribution {0} is already assigned to target message.error.creating.rollout = Server error. Error creating rollout. Please contact the administrator message.error.starting.rollout = Server error. Error starting rollout. Please contact the administrator + +#Target Filter Management +breadcrumb.target.filter.custom.filters = Custom Filters diff --git a/hawkbit-ui/src/main/resources/messages_en.properties b/hawkbit-ui/src/main/resources/messages_en.properties index 23df35ad2..23b8149e3 100644 --- a/hawkbit-ui/src/main/resources/messages_en.properties +++ b/hawkbit-ui/src/main/resources/messages_en.properties @@ -464,3 +464,6 @@ label.target.per.group = Targets per group : message.dist.already.assigned = Distribution {0} is already assigned to target message.error.creating.rollout = Server error. Error creating rollout. Please contact the administrator message.error.starting.rollout = Server error. Error starting rollout. Please contact the administrator + +#Target Filter Management +breadcrumb.target.filter.custom.filters = Custom Filters