diff --git a/hawkbit-runtime/.sandbox/README.md b/hawkbit-runtime/.sandbox/README.md index e31c02363..ae2a27579 100644 --- a/hawkbit-runtime/.sandbox/README.md +++ b/hawkbit-runtime/.sandbox/README.md @@ -3,15 +3,15 @@ hawkBit Sandbox ## Try out the update server in our hawkBit sandbox -- try out Management API https://hawkbit.eclipseprojects.io/rest/v1/targets (don't forget basic auth header; username: demo, passwd: demo) -- try out DDI API https://hawkbit.eclipseprojects.io/DEFAULT/controller/v1/MYTESTDEVICE - +- try out Management API https://hawkbit.eclipseprojects.io/rest/v1/targets (don't forget basic auth header; username: + demo, passwd: demo) +- try out DDI API https://hawkbit.eclipseprojects.io/DEFAULT/controller/v1/MYTESTDEVICE ## Sandbox Setup -**1. File Structure** +**1. File Structure** -Copy the files to the respective location on the VM. +Copy the files to the respective location on the VM. ``` / @@ -35,11 +35,12 @@ Reset the Sandbox once a week with a cron-job and log its output. $ sudo /.sandbox/scripts/initialize-cronjobs.sh ``` - **3. Nginx Reverse Proxy** -Start the stack for the Nginx reverse proxy with Let's Encrypt support. Thanks to JrCs for providing the Docker container - and instructions with his [Docker-Letsencrypt-Nginx-Companion](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion). +Start the stack for the Nginx reverse proxy with Let's Encrypt support. Thanks to JrCs for providing the Docker +container +and instructions with +his [Docker-Letsencrypt-Nginx-Companion](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion). ``` $ docker stack deploy -c /.sandbox/stacks/sandbox/docker-compose-stack.yml proxy diff --git a/hawkbit-runtime/.sandbox/stacks/proxy/docker-compose-stack.yml b/hawkbit-runtime/.sandbox/stacks/proxy/docker-compose-stack.yml index 5224f9cf4..e6796c413 100644 --- a/hawkbit-runtime/.sandbox/stacks/proxy/docker-compose-stack.yml +++ b/hawkbit-runtime/.sandbox/stacks/proxy/docker-compose-stack.yml @@ -27,21 +27,21 @@ services: nginx: image: "jwilder/nginx-proxy" ports: - - 80:80 - - 443:443 + - 80:80 + - 443:443 networks: - - proxy + - proxy volumes: - - certificates:/etc/nginx/certs:ro - - vhost:/etc/nginx/vhost.d - - challange:/usr/share/nginx/html - - /var/run/docker.sock:/tmp/docker.sock:ro + - certificates:/etc/nginx/certs:ro + - vhost:/etc/nginx/vhost.d + - challange:/usr/share/nginx/html + - /var/run/docker.sock:/tmp/docker.sock:ro deploy: replicas: 1 restart_policy: condition: on-failure labels: - - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy" + - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy" # --------------------- # lets encrypt nginx-proxy companion @@ -49,12 +49,12 @@ services: letsencrypt: image: "jrcs/letsencrypt-nginx-proxy-companion" networks: - - proxy + - proxy volumes: - - certificates:/etc/nginx/certs:rw - - /var/run/docker.sock:/var/run/docker.sock:ro - - vhost:/etc/nginx/vhost.d - - challange:/usr/share/nginx/html + - certificates:/etc/nginx/certs:rw + - /var/run/docker.sock:/var/run/docker.sock:ro + - vhost:/etc/nginx/vhost.d + - challange:/usr/share/nginx/html deploy: replicas: 1 restart_policy: diff --git a/hawkbit-runtime/.sandbox/stacks/sandbox/docker-compose-stack.yml b/hawkbit-runtime/.sandbox/stacks/sandbox/docker-compose-stack.yml index 1a0615121..296831078 100644 --- a/hawkbit-runtime/.sandbox/stacks/sandbox/docker-compose-stack.yml +++ b/hawkbit-runtime/.sandbox/stacks/sandbox/docker-compose-stack.yml @@ -28,18 +28,18 @@ services: hawkbit: image: "hawkbit/hawkbit-update-server:latest" networks: - - proxy_proxy - - sandbox + - proxy_proxy + - sandbox deploy: restart_policy: condition: on-failure environment: - - 'SPRING_PROFILES_ACTIVE=cloudsandbox' - - 'SPRING_RABBITMQ_HOST=rabbitmq' - - 'SPRING_RABBITMQ_USERNAME=guest' - - 'SPRING_RABBITMQ_PASSWORD=guest' - - 'VIRTUAL_HOST=hawkbit.eclipseprojects.io' - - 'LETSENCRYPT_HOST=hawkbit.eclipseprojects.io' + - 'SPRING_PROFILES_ACTIVE=cloudsandbox' + - 'SPRING_RABBITMQ_HOST=rabbitmq' + - 'SPRING_RABBITMQ_USERNAME=guest' + - 'SPRING_RABBITMQ_PASSWORD=guest' + - 'VIRTUAL_HOST=hawkbit.eclipseprojects.io' + - 'LETSENCRYPT_HOST=hawkbit.eclipseprojects.io' # --------------------- # hawkBit simulator @@ -47,16 +47,16 @@ services: simulator: image: "hawkbit/hawkbit-device-simulator:latest" networks: - - sandbox + - sandbox deploy: restart_policy: condition: on-failure environment: - - 'SPRING_RABBITMQ_VIRTUALHOST=/' - - 'SPRING_RABBITMQ_HOST=rabbitmq' - - 'SPRING_RABBITMQ_PORT=5672' - - 'SPRING_RABBITMQ_USERNAME=guest' - - 'SPRING_RABBITMQ_PASSWORD=guest' + - 'SPRING_RABBITMQ_VIRTUALHOST=/' + - 'SPRING_RABBITMQ_HOST=rabbitmq' + - 'SPRING_RABBITMQ_PORT=5672' + - 'SPRING_RABBITMQ_USERNAME=guest' + - 'SPRING_RABBITMQ_PASSWORD=guest' # --------------------- # RabbitMQ service @@ -64,7 +64,7 @@ services: rabbitmq: image: "rabbitmq:3-management" networks: - - sandbox + - sandbox deploy: restart_policy: condition: on-failure diff --git a/hawkbit-runtime/README.md b/hawkbit-runtime/README.md index 73c57c0ce..35217b6aa 100644 --- a/hawkbit-runtime/README.md +++ b/hawkbit-runtime/README.md @@ -1,15 +1,15 @@ hawkBit Runtime === +| Folder | Description | +|--------------------------|------------------------------------------------------------------------------------------------------------------------------| +| `.sandbox/` | Content of the hawkBit sandbox installation running on [hawkbit.eclipseprojects.io](https://hawkbit.eclipseprojects.io/UI/). | +| `docker/` | Docker related files, such es Dockerfiles, compose and stack files to quickly start up an hawkBit. | +| `docker/docker_build/` | Docker images build related files, such es Dockerfiles and build shell scripts. | +| `hawkbit-update-server/` | Spring-Boot application of hawkBit. Monolith containing all services. | +| `hawkbit-ddi-server/` | Spring-Boot application of hawkBit DDI server. | +| `hawkbit-dmf-server/` | Spring-Boot application of hawkBit DMF server. | +| `hawkbit-mgmt-server/` | Spring-Boot application of hawkBit Management server. Provides REST Management API and rollouts / auto assigment processing | -| Folder | Description | -|--------------------------|----------------------------------------------------------------------------------------------------------------------------| -| `.sandbox/` | Content of the hawkBit sandbox installation running on [hawkbit.eclipseprojects.io](https://hawkbit.eclipseprojects.io/UI/). | -| `docker/` | Docker related files, such es Dockerfiles, compose and stack files to quickly start up an hawkBit. | -| `docker/docker_build/` | Docker images build related files, such es Dockerfiles and build shell scripts. | -| `hawkbit-update-server/` | Spring-Boot application of hawkBit. Monolith containing all services. | -| `hawkbit-ddi-server/` | Spring-Boot application of hawkBit DDI server. | -| `hawkbit-dmf-server/` | Spring-Boot application of hawkBit DMF server. | -| `hawkbit-mgmt-server/` | Spring-Boot application of hawkBit Management server. Provides REST Management API and rollouts / auto assigment processing | - -Note: micro service setup requires all services using DB to use same shared DB. So, they don't work with default in memory H2 database. Docker compose with mysql shows an example setup. +Note: micro service setup requires all services using DB to use same shared DB. So, they don't work with default in +memory H2 database. Docker compose with mysql shows an example setup. diff --git a/hawkbit-runtime/docker/README.md b/hawkbit-runtime/docker/README.md index 5ec99eb4f..5544fa3e9 100644 --- a/hawkbit-runtime/docker/README.md +++ b/hawkbit-runtime/docker/README.md @@ -20,10 +20,10 @@ Start the hawkBit Update Server together with an MySQL and RabbitMQ instance as $ docker-compose up -d ``` - ## C: Docker Stack -Start the hawkBit Update Server and Device Simulator together with an MySQL and RabbitMQ instance as services within a swarm +Start the hawkBit Update Server and Device Simulator together with an MySQL and RabbitMQ instance as services within a +swarm ```bash $ docker swarm init @@ -32,16 +32,18 @@ $ docker stack deploy -c docker-compose-deps-mysql.yml hawkbit # Access -| Service / Container | URL | Login | A | B | C | -|---|---|---|---|---|---| -| hawkBit Update Server | [http://localhost:8080/](http://localhost:8080/) | admin:admin | ✓ | ✓ | ✓ | -| hawkBit Device Simulator | [http://localhost:8083/](http://localhost:8083/) | - | | | ✓ | -| MySQL | localhost:3306/hawkbit | root | | ✓ | ✓ | -| RabbitMQ | [http://localhost:15672](http://localhost:15672) | guest:guest | | ✓ | ✓ | +| Service / Container | URL | Login | A | B | C | +|--------------------------|--------------------------------------------------|-------------|----------|----------|----------| +| hawkBit Update Server | [http://localhost:8080/](http://localhost:8080/) | admin:admin | ✓ | ✓ | ✓ | +| hawkBit Device Simulator | [http://localhost:8083/](http://localhost:8083/) | - | | | ✓ | +| MySQL | localhost:3306/hawkbit | root | | ✓ | ✓ | +| RabbitMQ | [http://localhost:15672](http://localhost:15672) | guest:guest | | ✓ | ✓ | # Configuration -You can override application.properties by setting an environment variable SPRING_APPLICATION_JSON for hawkbit container. +You can override application.properties by setting an environment variable SPRING_APPLICATION_JSON for hawkbit +container. + ``` hawkbit: image: "hawkbit/hawkbit-update-server:latest-mysql" diff --git a/hawkbit-runtime/docker/docker_build/README.md b/hawkbit-runtime/docker/docker_build/README.md index 62363ce38..13fb24ad9 100644 --- a/hawkbit-runtime/docker/docker_build/README.md +++ b/hawkbit-runtime/docker/docker_build/README.md @@ -1,55 +1,82 @@ hawkBit Docker Build === This directory contains docker files for building both hawkBit docker flavours: + * _standard_ - hawkBit images without mysql driver * _mysql_ - with MariaDB Java connector with support for MySQL. - -Both flavours of are almost the same, just mysql has in addition a MariaDB Java connector and is started, by default, with mysql Spring profile. + +Both flavours of are almost the same, just mysql has in addition a MariaDB Java connector and is started, by default, +with mysql Spring profile. For every flavour there are two build types: + * _release_ - uses officially released hawkBit versions, downloading them from https://repo1.maven.org -* _development/dev_ - uses the local maven repository with built by developer (or just downloaded from any maven repository) hawkBit applications +* _development/dev_ - uses the local maven repository with built by developer (or just downloaded from any maven + repository) hawkBit applications ## Build overview + Building images supports the following build arguments (i.e. ARG-s which could be passed using _--build-arg_): -* _JAVA_VERSION_ - **[OPTIONAL, if not set a default is used]** the Java version of the eclipse-temurin jre-alpine base image to be used. -* _HAWKBIT_APP_ - **[OPTIONAL, if not set _hawkbit-update-server_ is used]** the application to be build. Currently, there is just _hawkbit-update-server_ but in future, if hawkBit is split to micro-services, there could be different micro-service apps. -* _HAWKBIT_VERSION_ - **[OPTIONAL, if not set a default, should be the last officially released version, is used]** the application version + +* _JAVA_VERSION_ - **[OPTIONAL, if not set a default is used]** the Java version of the eclipse-temurin jre-alpine base + image to be used. +* _HAWKBIT_APP_ - **[OPTIONAL, if not set _hawkbit-update-server_ is used]** the application to be build. Currently, + there is just _hawkbit-update-server_ but in future, if hawkBit is split to micro-services, there could be different + micro-service apps. +* _HAWKBIT_VERSION_ - **[OPTIONAL, if not set a default, should be the last officially released version, is used]** the + application version * _CONTAINER_PORT_ - **[OPTIONAL, if not set 8080 is used]** on which the app opens the http server (if available) -* _MARIADB_DRIVER_VERSION_ (mysql flavours only!) - **[OPTIONAL, if not set a default is used]** the version of MariaDB connector to be used +* _MARIADB_DRIVER_VERSION_ (mysql flavours only!) - **[OPTIONAL, if not set a default is used]** the version of MariaDB + connector to be used Additionally, tge _development_ builds shall be started with docker build context the local maven repository ## Build standard + Standard flavour could be build, for example, with (fixed version 0.4.1 is just an example): + ```shell docker build --build-arg HAWKBIT_APP=hawkbit-update-server --build-arg HAWKBIT_VERSION=0.4.1 -t hawkbit_update_server:0.4.1 . -f Dockerfile ``` + or just by: + ```shell docker build --build-arg HAWKBIT_VERSION=0.4.1 -t hawkbit_update_server:0.4.1 . ``` + having that docker uses by default _Dockerfile_ and the _hawkbit-update-server_ is the default _HAWKBIT_APP_. To build standard development docker images, e.g. snapshot based, you could use something like: + ```shell docker build -t hawkbit_update_server:0-SNAPSHOT -f Dockerfile_dev ~/.m2/repository ``` -Note that here you have to use your maven repository containing the hawkBit app as docker build context, in the example case _~/.m2/repository_ + +Note that here you have to use your maven repository containing the hawkBit app as docker build context, in the example +case _~/.m2/repository_ ## Build mysql + Mysql flavour could be build, for example, with: + ```shell docker build --build-arg HAWKBIT_APP=hawkbit-update-server --build-arg HAWKBIT_VERSION=0.4.1 -t hawkbit_update_server:0.4.1-mysql . -f Dockerfile-mysql ``` + or just by: + ```shell docker build --build-arg -t hawkbit_update_server:0.4.1-mysql --build-arg HAWKBIT_VERSION=0.4.1 . -f Dockerfile-mysql ``` + having that the _hawkbit-update-server_ is the default _HAWKBIT_APP_. To build development mysql docker images, e.g. snapshot based, you could use something like: + ```shell docker build -t hawkbit_update_server:0-SNAPSHOT-mysql -f Dockerfile_dev-mysql ~/.m2/repository ``` -Note that here you have to use your maven repository containing the hawkBit app as docker build context, in the example case _~/.m2/repository_ \ No newline at end of file + +Note that here you have to use your maven repository containing the hawkBit app as docker build context, in the example +case _~/.m2/repository_ \ No newline at end of file diff --git a/hawkbit-runtime/hawkbit-ddi-server/README.md b/hawkbit-runtime/hawkbit-ddi-server/README.md index b88594dfc..08387c79b 100644 --- a/hawkbit-runtime/hawkbit-ddi-server/README.md +++ b/hawkbit-runtime/hawkbit-ddi-server/README.md @@ -1,5 +1,7 @@ # hawkBit DDI Server (EXPERIMENTAL!) -The hawkBit DDI Server is a standalone spring-boot application with an embedded servlet container. It should be started with at least hawkbit-mgmt-server. + +The hawkBit DDI Server is a standalone spring-boot application with an embedded servlet container. It should be started +with at least hawkbit-mgmt-server. ## On your own workstation @@ -18,14 +20,22 @@ run org.eclipse.hawkbit.app.ddi.DDIStart ``` ### Usage + The Management API can be accessed via http://localhost:8081/rest/v1 The root url http://localhost:8081 will redirect directly to the Swagger Management UI ### Clustering (Experimental!!!) -The micro-service instances are configured to communicate via Spring Cloud Bus. You could run multiple instances of any micro-service but hawkbit-mgmt-server. Management server run some schedulers which shall not run simultaneously - e.g. auto assignment checker and rollouts executor. To run multiple management server instances you shall do some extensions of hawkbit to ensure that they wont run schedulers simultaneously or you shall configure all instances but one to do not run schedulers! + +The micro-service instances are configured to communicate via Spring Cloud Bus. You could run multiple instances of any +micro-service but hawkbit-mgmt-server. Management server run some schedulers which shall not run simultaneously - e.g. +auto assignment checker and rollouts executor. To run multiple management server instances you shall do some extensions +of hawkbit to ensure that they wont run schedulers simultaneously or you shall configure all instances but one to do not +run schedulers! ## Optional Protostuff for Sprign cloud bus -The micro-service instances are configured to communicate via Spring Cloud Bus. Optionally, you could use [Protostuff](https://github.com/protostuff/protostuff) based message payload serialization for improved performance. + +The micro-service instances are configured to communicate via Spring Cloud Bus. Optionally, you could +use [Protostuff](https://github.com/protostuff/protostuff) based message payload serialization for improved performance. **Note**: If Protostuff is enabled it shall be enabled on all microservices! diff --git a/hawkbit-runtime/hawkbit-ddi-server/pom.xml b/hawkbit-runtime/hawkbit-ddi-server/pom.xml index c9462c871..472935aa0 100644 --- a/hawkbit-runtime/hawkbit-ddi-server/pom.xml +++ b/hawkbit-runtime/hawkbit-ddi-server/pom.xml @@ -9,7 +9,7 @@ SPDX-License-Identifier: EPL-2.0 --> - 4.0.0 @@ -30,7 +30,7 @@ hawkbit-boot-starter-ddi-api ${project.version} - + org.springframework.cloud spring-cloud-stream-binder-rabbit diff --git a/hawkbit-runtime/hawkbit-ddi-server/src/main/java/org/eclipse/hawkbit/app/ddi/DDIStart.java b/hawkbit-runtime/hawkbit-ddi-server/src/main/java/org/eclipse/hawkbit/app/ddi/DDIStart.java index 2de858845..6f46b5471 100644 --- a/hawkbit-runtime/hawkbit-ddi-server/src/main/java/org/eclipse/hawkbit/app/ddi/DDIStart.java +++ b/hawkbit-runtime/hawkbit-ddi-server/src/main/java/org/eclipse/hawkbit/app/ddi/DDIStart.java @@ -19,7 +19,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.view.RedirectView; - /** * A {@link SpringBootApplication} annotated class with a main method to start. * The minimal configuration for the stand alone hawkBit DDI server. @@ -31,8 +30,7 @@ public class DDIStart { /** * Main method to start the spring-boot application. * - * @param args - * the VM arguments. + * @param args the VM arguments. */ public static void main(final String[] args) { SpringApplication.run(DDIStart.class, args); diff --git a/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AbstractSecurityTest.java b/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AbstractSecurityTest.java index 04b2934ef..434ac9680 100644 --- a/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AbstractSecurityTest.java +++ b/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AbstractSecurityTest.java @@ -24,11 +24,10 @@ import org.springframework.web.context.WebApplicationContext; @ExtendWith(SharedSqlTestDatabaseExtension.class) public abstract class AbstractSecurityTest { + protected MockMvc mvc; @Autowired private WebApplicationContext context; - protected MockMvc mvc; - @BeforeEach public void setup() { final DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(context) diff --git a/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AllowedHostNamesTest.java b/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AllowedHostNamesTest.java index c425a852b..a0a556fc6 100644 --- a/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AllowedHostNamesTest.java +++ b/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/AllowedHostNamesTest.java @@ -9,17 +9,14 @@ */ package org.eclipse.hawkbit.app.ddi; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import io.qameta.allure.Description; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpHeaders; -import org.springframework.security.web.firewall.RequestRejectedException; - import io.qameta.allure.Feature; import io.qameta.allure.Story; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; import org.springframework.test.context.TestPropertySource; @TestPropertySource(properties = { "hawkbit.server.security.allowedHostNames=localhost", diff --git a/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/PreAuthorizeEnabledTest.java b/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/PreAuthorizeEnabledTest.java index c51fc3f4d..809be7ade 100644 --- a/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/PreAuthorizeEnabledTest.java +++ b/hawkbit-runtime/hawkbit-ddi-server/src/test/java/org/eclipse/hawkbit/app/ddi/PreAuthorizeEnabledTest.java @@ -9,6 +9,9 @@ */ package org.eclipse.hawkbit.app.ddi; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + import io.qameta.allure.Description; import io.qameta.allure.Feature; import io.qameta.allure.Story; @@ -18,25 +21,22 @@ import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; import org.springframework.test.context.TestPropertySource; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; - @Feature("Integration Test - Security") @Story("PreAuthorized enabled") -@TestPropertySource(properties = {"spring.flyway.enabled=true"}) +@TestPropertySource(properties = { "spring.flyway.enabled=true" }) public class PreAuthorizeEnabledTest extends AbstractSecurityTest { @Test @Description("Tests whether request fail if a role is forbidden for the user") - @WithUser(authorities = { SpPermission.READ_TARGET } ) + @WithUser(authorities = { SpPermission.READ_TARGET }) public void failIfNoRole() throws Exception { mvc.perform(get("/DEFAULT/controller/v1/controllerId")).andExpect(result -> - assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value())); + assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value())); } @Test @Description("Tests whether request succeed if a role is granted for the user") - @WithUser(authorities = { SpPermission.SpringEvalExpressions.CONTROLLER_ROLE }) + @WithUser(authorities = { SpPermission.SpringEvalExpressions.CONTROLLER_ROLE }) public void successIfHasRole() throws Exception { mvc.perform(get("/DEFAULT/controller/v1/controllerId")).andExpect(result -> { assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()); diff --git a/hawkbit-runtime/hawkbit-dmf-server/README.md b/hawkbit-runtime/hawkbit-dmf-server/README.md index 58c78e185..297c4d324 100644 --- a/hawkbit-runtime/hawkbit-dmf-server/README.md +++ b/hawkbit-runtime/hawkbit-dmf-server/README.md @@ -1,5 +1,7 @@ # hawkBit DMF Server (EXPERIMENTAL!) -The hawkBit DMF Server is a standalone spring-boot application with an embedded servlet container. It should be started with at least hawkbit-mgmt-server. + +The hawkBit DMF Server is a standalone spring-boot application with an embedded servlet container. It should be started +with at least hawkbit-mgmt-server. ## On your own workstation @@ -18,10 +20,17 @@ run org.eclipse.hawkbit.app.dmf.DMFStart ``` ### Clustering (Experimental!!!) -The micro-service instances are configured to communicate via Spring Cloud Bus. You could run multiple instances of any micro-service but hawkbit-mgmt-server. Management server run some schedulers which shall not run simultaneously - e.g. auto assignment checker and rollouts executor. To run multiple management server instances you shall do some extensions of hawkbit to ensure that they wont run schedulers simultaneously or you shall configure all instances but one to do not run schedulers! + +The micro-service instances are configured to communicate via Spring Cloud Bus. You could run multiple instances of any +micro-service but hawkbit-mgmt-server. Management server run some schedulers which shall not run simultaneously - e.g. +auto assignment checker and rollouts executor. To run multiple management server instances you shall do some extensions +of hawkbit to ensure that they wont run schedulers simultaneously or you shall configure all instances but one to do not +run schedulers! ## Optional Protostuff for Sprign cloud bus -The micro-service instances are configured to communicate via Spring Cloud Bus. Optionally, you could use [Protostuff](https://github.com/protostuff/protostuff) based message payload serialization for improved performance. + +The micro-service instances are configured to communicate via Spring Cloud Bus. Optionally, you could +use [Protostuff](https://github.com/protostuff/protostuff) based message payload serialization for improved performance. **Note**: If Protostuff is enabled it shall be enabled on all microservices! diff --git a/hawkbit-runtime/hawkbit-dmf-server/pom.xml b/hawkbit-runtime/hawkbit-dmf-server/pom.xml index 92f0e4aea..45f31c7bd 100644 --- a/hawkbit-runtime/hawkbit-dmf-server/pom.xml +++ b/hawkbit-runtime/hawkbit-dmf-server/pom.xml @@ -9,7 +9,7 @@ SPDX-License-Identifier: EPL-2.0 --> - 4.0.0 diff --git a/hawkbit-runtime/hawkbit-mgmt-server/README.md b/hawkbit-runtime/hawkbit-mgmt-server/README.md index 3fd6abdb9..dce12d6c9 100644 --- a/hawkbit-runtime/hawkbit-mgmt-server/README.md +++ b/hawkbit-runtime/hawkbit-mgmt-server/README.md @@ -1,5 +1,7 @@ # hawkBit Management Server (EXPERIMENTAL!) -The hawkBit Management Server is a standalone spring-boot application with an embedded servlet container. It should be started with at least one (or both) of the device interface servers - hawkbit-ddi-server or/and hawkbit-dmf-server. + +The hawkBit Management Server is a standalone spring-boot application with an embedded servlet container. It should be +started with at least one (or both) of the device interface servers - hawkbit-ddi-server or/and hawkbit-dmf-server. ## On your own workstation @@ -18,14 +20,22 @@ run org.eclipse.hawkbit.app.mgmt.MgmtServerStart ``` ### Usage + The Management API can be accessed via http://localhost:8080/rest/v1 The root url http://localhost:8080 will redirect directly to the Swagger Management UI ### Clustering (Experimental!!!) -The micro-service instances are configured to communicate via Spring Cloud Bus. You could run multiple instances of any micro-service but hawkbit-mgmt-server. Management server run some schedulers which shall not run simultaneously - e.g. auto assignment checker and rollouts executor. To run multiple management server instances you shall do some extensions of hawkbit to ensure that they wont run schedulers simultaneously or you shall configure all instances but one to do not run schedulers! + +The micro-service instances are configured to communicate via Spring Cloud Bus. You could run multiple instances of any +micro-service but hawkbit-mgmt-server. Management server run some schedulers which shall not run simultaneously - e.g. +auto assignment checker and rollouts executor. To run multiple management server instances you shall do some extensions +of hawkbit to ensure that they wont run schedulers simultaneously or you shall configure all instances but one to do not +run schedulers! ## Optional Protostuff for Sprign cloud bus -The micro-service instances are configured to communicate via Spring Cloud Bus. Optionally, you could use [Protostuff](https://github.com/protostuff/protostuff) based message payload serialization for improved performance. + +The micro-service instances are configured to communicate via Spring Cloud Bus. Optionally, you could +use [Protostuff](https://github.com/protostuff/protostuff) based message payload serialization for improved performance. **Note**: If Protostuff is enabled it shall be enabled on all microservices! diff --git a/hawkbit-runtime/hawkbit-mgmt-server/pom.xml b/hawkbit-runtime/hawkbit-mgmt-server/pom.xml index a98365f57..8d57dc44d 100644 --- a/hawkbit-runtime/hawkbit-mgmt-server/pom.xml +++ b/hawkbit-runtime/hawkbit-mgmt-server/pom.xml @@ -9,7 +9,7 @@ SPDX-License-Identifier: EPL-2.0 --> - 4.0.0 diff --git a/hawkbit-runtime/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/ErrorController.java b/hawkbit-runtime/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/ErrorController.java index 1a9dc6c28..8f6250ccf 100644 --- a/hawkbit-runtime/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/ErrorController.java +++ b/hawkbit-runtime/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/ErrorController.java @@ -36,11 +36,9 @@ public class ErrorController extends BasicErrorController { /** * A new {@link ErrorController}. - * - * @param errorAttributes - * the error attributes - * @param serverProperties - * configuration properties + * + * @param errorAttributes the error attributes + * @param serverProperties configuration properties */ public ErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) { super(errorAttributes, serverProperties.getError()); diff --git a/hawkbit-runtime/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/MgmtServerStart.java b/hawkbit-runtime/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/MgmtServerStart.java index 32f11d313..3c8a11e3d 100644 --- a/hawkbit-runtime/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/MgmtServerStart.java +++ b/hawkbit-runtime/hawkbit-mgmt-server/src/main/java/org/eclipse/hawkbit/app/mgmt/MgmtServerStart.java @@ -22,7 +22,6 @@ import org.springframework.web.servlet.view.RedirectView; /** * A {@link SpringBootApplication} annotated class with a main method to start. * The minimal configuration for the stand alone hawkBit server. - * */ @SpringBootApplication(scanBasePackages = "org.eclipse.hawkbit") @EnableHawkbitManagedSecurityConfiguration diff --git a/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AbstractSecurityTest.java b/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AbstractSecurityTest.java index c307d27b7..8f055259c 100644 --- a/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AbstractSecurityTest.java +++ b/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AbstractSecurityTest.java @@ -24,11 +24,10 @@ import org.springframework.web.context.WebApplicationContext; @ExtendWith(SharedSqlTestDatabaseExtension.class) public abstract class AbstractSecurityTest { + protected MockMvc mvc; @Autowired private WebApplicationContext context; - protected MockMvc mvc; - @BeforeEach public void setup() { final DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(context) diff --git a/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AllowedHostNamesTest.java b/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AllowedHostNamesTest.java index 34f38f87b..1ee779c05 100644 --- a/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AllowedHostNamesTest.java +++ b/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/AllowedHostNamesTest.java @@ -9,17 +9,14 @@ */ package org.eclipse.hawkbit.app.mgmt; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import io.qameta.allure.Description; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpHeaders; -import org.springframework.security.web.firewall.RequestRejectedException; - import io.qameta.allure.Feature; import io.qameta.allure.Story; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; import org.springframework.test.context.TestPropertySource; @TestPropertySource(properties = { "hawkbit.server.security.allowedHostNames=localhost", diff --git a/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java b/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java index fcc0183d2..e927360bd 100644 --- a/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java +++ b/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/CorsTest.java @@ -14,19 +14,17 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import io.qameta.allure.Description; +import io.qameta.allure.Feature; +import io.qameta.allure.Story; import org.eclipse.hawkbit.im.authentication.SpRole; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; - import org.eclipse.hawkbit.repository.test.util.WithUser; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpHeaders; import org.springframework.test.web.servlet.ResultActions; -import io.qameta.allure.Description; -import io.qameta.allure.Feature; -import io.qameta.allure.Story; - @SpringBootTest( properties = { "hawkbit.dmf.rabbitmq.enabled=false", @@ -35,7 +33,7 @@ import io.qameta.allure.Story; CorsTest.ALLOWED_ORIGIN_FIRST + "," + CorsTest.ALLOWED_ORIGIN_SECOND, "hawkbit.server.security.cors.exposedHeaders=" + - HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN}) + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN }) @Feature("Integration Test - Security") @Story("CORS") public class CorsTest extends AbstractSecurityTest { @@ -63,8 +61,8 @@ public class CorsTest extends AbstractSecurityTest { final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin( MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden()) - .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn() - .getResponse().getContentAsString(); + .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn() + .getResponse().getContentAsString(); assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST); } diff --git a/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/PreAuthorizeEnabledTest.java b/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/PreAuthorizeEnabledTest.java index 8c3f5b070..736e6f121 100644 --- a/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/PreAuthorizeEnabledTest.java +++ b/hawkbit-runtime/hawkbit-mgmt-server/src/test/java/org/eclipse/hawkbit/app/mgmt/PreAuthorizeEnabledTest.java @@ -9,6 +9,11 @@ */ package org.eclipse.hawkbit.app.mgmt; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + +import java.util.HashMap; + import com.fasterxml.jackson.databind.ObjectMapper; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -19,26 +24,21 @@ import org.eclipse.hawkbit.repository.test.util.WithUser; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; -import java.util.HashMap; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; - @Feature("Integration Test - Security") @Story("PreAuthorized enabled") public class PreAuthorizeEnabledTest extends AbstractSecurityTest { @Test @Description("Tests whether request fail if a role is forbidden for the user") - @WithUser(authorities = { SpPermission.READ_TARGET } ) + @WithUser(authorities = { SpPermission.READ_TARGET }) public void failIfNoRole() throws Exception { mvc.perform(get("/rest/v1/distributionsets")).andExpect(result -> - assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value())); + assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value())); } @Test @Description("Tests whether request succeed if a role is granted for the user") - @WithUser(authorities = { SpPermission.READ_REPOSITORY }) + @WithUser(authorities = { SpPermission.READ_REPOSITORY }) public void successIfHasRole() throws Exception { mvc.perform(get("/rest/v1/distributionsets")).andExpect(result -> { assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()); @@ -47,7 +47,7 @@ public class PreAuthorizeEnabledTest extends AbstractSecurityTest { @Test @Description("Tests whether request succeed if a role is granted for the user") - @WithUser(authorities = { SpRole.TENANT_ADMIN }) + @WithUser(authorities = { SpRole.TENANT_ADMIN }) public void successIfHasTenantAdminRole() throws Exception { mvc.perform(get("/rest/v1/distributionsets")).andExpect(result -> { assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()); @@ -57,7 +57,7 @@ public class PreAuthorizeEnabledTest extends AbstractSecurityTest { @Test @Description("Tests whether read tenant config request fail if a tenant config (or read read) is not " + "granted for the user") - @WithUser(authorities = { SpPermission.READ_TARGET } ) + @WithUser(authorities = { SpPermission.READ_TARGET }) public void onlyDSIfNoTenantConfig() throws Exception { mvc.perform(get("/rest/v1/system/configs")).andExpect(result -> { // returns default DS type because of READ_TARGET @@ -72,9 +72,9 @@ public class PreAuthorizeEnabledTest extends AbstractSecurityTest { @Test @Description("Tests whether read tenant config request succeed if a tenant config (not read explicitly) is " + "granted for the user") - @WithUser(authorities = { SpPermission.TENANT_CONFIGURATION }) + @WithUser(authorities = { SpPermission.TENANT_CONFIGURATION }) public void successIfHasTenantConfig() throws Exception { mvc.perform(get("/rest/v1/system/configs")).andExpect(result -> - assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value())); + assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value())); } } \ No newline at end of file diff --git a/hawkbit-runtime/hawkbit-simple-ui/pom.xml b/hawkbit-runtime/hawkbit-simple-ui/pom.xml index 9ff55073c..ff3cfb46a 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/pom.xml +++ b/hawkbit-runtime/hawkbit-simple-ui/pom.xml @@ -9,8 +9,9 @@ SPDX-License-Identifier: EPL-2.0 --> - + 4.0.0 org.eclipse.hawkbit diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/HawkbitMgmtClient.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/HawkbitMgmtClient.java index 76f83b730..e122bd00d 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/HawkbitMgmtClient.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/HawkbitMgmtClient.java @@ -9,8 +9,8 @@ */ package org.eclipse.hawkbit.ui.simple; -import org.eclipse.hawkbit.sdk.HawkbitClient; -import org.eclipse.hawkbit.sdk.Tenant; +import java.util.function.Supplier; + import feign.FeignException; import lombok.Getter; import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi; @@ -24,8 +24,9 @@ import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTagRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetTypeRestApi; import org.eclipse.hawkbit.mgmt.rest.api.MgmtTenantManagementRestApi; +import org.eclipse.hawkbit.sdk.HawkbitClient; +import org.eclipse.hawkbit.sdk.Tenant; import org.springframework.http.ResponseEntity; -import java.util.function.Supplier; @Getter public class HawkbitMgmtClient { @@ -49,7 +50,7 @@ public class HawkbitMgmtClient { this.tenant = tenant; this.hawkbitClient = hawkbitClient; - softwareModuleRestApi = service(MgmtSoftwareModuleRestApi .class); + softwareModuleRestApi = service(MgmtSoftwareModuleRestApi.class); softwareModuleTypeRestApi = service(MgmtSoftwareModuleTypeRestApi.class); distributionSetRestApi = service(MgmtDistributionSetRestApi.class); distributionSetTypeRestApi = service(MgmtDistributionSetTypeRestApi.class); @@ -77,6 +78,7 @@ public class HawkbitMgmtClient { boolean hasTargetRead() { return hasRead(() -> targetRestApi.getTarget("_#ETE$ER")); } + boolean hasConfigRead() { return hasRead(() -> tenantManagementRestApi.getTenantConfigurationValue("_#ETE$ER")); } diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/MainLayout.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/MainLayout.java index a98868806..9f22500ad 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/MainLayout.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/MainLayout.java @@ -9,13 +9,8 @@ */ package org.eclipse.hawkbit.ui.simple; -import org.eclipse.hawkbit.ui.simple.view.TargetView; -import org.eclipse.hawkbit.ui.simple.view.RolloutView; -import org.eclipse.hawkbit.ui.simple.security.AuthenticatedUser; -import org.eclipse.hawkbit.ui.simple.view.AboutView; -import org.eclipse.hawkbit.ui.simple.view.ConfigView; -import org.eclipse.hawkbit.ui.simple.view.DistributionSetView; -import org.eclipse.hawkbit.ui.simple.view.SoftwareModuleView; +import java.util.Optional; + import com.vaadin.flow.component.Unit; import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.applayout.DrawerToggle; @@ -39,18 +34,22 @@ import com.vaadin.flow.component.sidenav.SideNavItem; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.server.auth.AccessAnnotationChecker; import com.vaadin.flow.theme.lumo.LumoUtility; - -import java.util.Optional; +import org.eclipse.hawkbit.ui.simple.security.AuthenticatedUser; +import org.eclipse.hawkbit.ui.simple.view.AboutView; +import org.eclipse.hawkbit.ui.simple.view.ConfigView; +import org.eclipse.hawkbit.ui.simple.view.DistributionSetView; +import org.eclipse.hawkbit.ui.simple.view.RolloutView; +import org.eclipse.hawkbit.ui.simple.view.SoftwareModuleView; +import org.eclipse.hawkbit.ui.simple.view.TargetView; /** * The main view is a top-level placeholder for other views. */ public class MainLayout extends AppLayout { - private H2 viewTitle; - private final transient AuthenticatedUser authenticatedUser; private final AccessAnnotationChecker accessChecker; + private H2 viewTitle; public MainLayout(final AuthenticatedUser authenticatedUser, final AccessAnnotationChecker accessChecker) { this.authenticatedUser = authenticatedUser; @@ -62,6 +61,15 @@ public class MainLayout extends AppLayout { addHeaderContent(); } + @Override + protected void afterNavigation() { + super.afterNavigation(); + viewTitle.setText( + Optional.ofNullable(getContent().getClass().getAnnotation(PageTitle.class)) + .map(PageTitle::value) + .orElse("")); + } + private void addHeaderContent() { final DrawerToggle toggle = new DrawerToggle(); toggle.setAriaLabel("Menu toggle"); @@ -143,13 +151,4 @@ public class MainLayout extends AppLayout { return layout; } - - @Override - protected void afterNavigation() { - super.afterNavigation(); - viewTitle.setText( - Optional.ofNullable(getContent().getClass().getAnnotation(PageTitle.class)) - .map(PageTitle::value) - .orElse("")); - } } diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/SimpleUIApp.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/SimpleUIApp.java index 3e3e1452d..a956d181f 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/SimpleUIApp.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/SimpleUIApp.java @@ -9,6 +9,13 @@ */ package org.eclipse.hawkbit.ui.simple; +import static feign.Util.ISO_8859_1; + +import java.util.Base64; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + import com.vaadin.flow.component.page.AppShellConfigurator; import com.vaadin.flow.server.PWA; import com.vaadin.flow.theme.Theme; @@ -35,15 +42,8 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.util.Base64; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; - -import static feign.Util.ISO_8859_1; - @Theme(themeClass = Lumo.class) -@PWA(name="hawkBit UI", shortName="hawkBit UI") +@PWA(name = "hawkBit UI", shortName = "hawkBit UI") @SpringBootApplication @Import(FeignClientsConfiguration.class) public class SimpleUIApp implements AppShellConfigurator { @@ -74,8 +74,8 @@ public class SimpleUIApp implements AppShellConfigurator { hawkBitServer, client, encoder, decoder, contract, ERROR_DECODER, (tenant, controller) -> - controller == null ? - AUTHORIZATION : HawkbitClient.DEFAULT_REQUEST_INTERCEPTOR_FN.apply(tenant, controller)); + controller == null ? + AUTHORIZATION : HawkbitClient.DEFAULT_REQUEST_INTERCEPTOR_FN.apply(tenant, controller)); } @Bean @@ -86,45 +86,46 @@ public class SimpleUIApp implements AppShellConfigurator { // accepts all user / pass, just delegating them to the feign client @Bean AuthenticationManager authenticationManager(final HawkbitMgmtClient hawkbitClient) { - return authentication-> { - final String username = authentication.getName(); - final String password = authentication.getCredentials().toString(); + return authentication -> { + final String username = authentication.getName(); + final String password = authentication.getCredentials().toString(); - final List roles = new LinkedList<>(); - roles.add("ANONYMOUS"); - final SecurityContext unauthorizedContext = SecurityContextHolder.createEmptyContext(); - unauthorizedContext.setAuthentication( - new UsernamePasswordAuthenticationToken(username, password)); - final SecurityContext currentContext = SecurityContextHolder.getContext(); - try { - SecurityContextHolder.setContext(unauthorizedContext); - if (hawkbitClient.hasSoftwareModulesRead()) { - roles.add("SOFTWARE_MODULE_READ"); - } - if (hawkbitClient.hasRolloutRead()) { - roles.add("ROLLOUT_READ"); - } - if (hawkbitClient.hasDistributionSetRead()) { - roles.add("DISTRIBUTION_SET_READ"); - } - if (hawkbitClient.hasTargetRead()) { - roles.add("TARGET_READ"); - } - if (hawkbitClient.hasConfigRead()) { - roles.add("CONFIG_READ"); - } - } finally { - SecurityContextHolder.setContext(currentContext); + final List roles = new LinkedList<>(); + roles.add("ANONYMOUS"); + final SecurityContext unauthorizedContext = SecurityContextHolder.createEmptyContext(); + unauthorizedContext.setAuthentication( + new UsernamePasswordAuthenticationToken(username, password)); + final SecurityContext currentContext = SecurityContextHolder.getContext(); + try { + SecurityContextHolder.setContext(unauthorizedContext); + if (hawkbitClient.hasSoftwareModulesRead()) { + roles.add("SOFTWARE_MODULE_READ"); + } + if (hawkbitClient.hasRolloutRead()) { + roles.add("ROLLOUT_READ"); + } + if (hawkbitClient.hasDistributionSetRead()) { + roles.add("DISTRIBUTION_SET_READ"); + } + if (hawkbitClient.hasTargetRead()) { + roles.add("TARGET_READ"); + } + if (hawkbitClient.hasConfigRead()) { + roles.add("CONFIG_READ"); + } + } finally { + SecurityContextHolder.setContext(currentContext); + } + return new UsernamePasswordAuthenticationToken( + username, password, + roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).toList()) { + + @Override + public void eraseCredentials() { + // don't erase credentials because they will be used + // to authenticate to the hawkBit update server / mgmt server } - return new UsernamePasswordAuthenticationToken( - username, password, - roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).toList()) { - @Override - public void eraseCredentials() { - // don't erase credentials because they will be used - // to authenticate to the hawkBit update server / mgmt server - } - }; }; + }; } } diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/security/AuthenticatedUser.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/security/AuthenticatedUser.java index 0e39188ba..c679f70a7 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/security/AuthenticatedUser.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/security/AuthenticatedUser.java @@ -9,13 +9,14 @@ */ package org.eclipse.hawkbit.ui.simple.security; +import java.util.Optional; + import com.vaadin.flow.spring.security.AuthenticationContext; import org.springframework.stereotype.Component; -import java.util.Optional; - @Component public class AuthenticatedUser { + private final AuthenticationContext authenticationContext; public AuthenticatedUser(final AuthenticationContext authenticationContext) { diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/security/SecurityConfiguration.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/security/SecurityConfiguration.java index d0c3d4f73..12563a915 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/security/SecurityConfiguration.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/security/SecurityConfiguration.java @@ -9,8 +9,8 @@ */ package org.eclipse.hawkbit.ui.simple.security; -import org.eclipse.hawkbit.ui.simple.view.LoginView; import com.vaadin.flow.spring.security.VaadinWebSecurity; +import org.eclipse.hawkbit.ui.simple.view.LoginView; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -33,7 +33,7 @@ public class SecurityConfiguration extends VaadinWebSecurity { http.authorizeHttpRequests( authorize -> authorize.requestMatchers(new AntPathRequestMatcher("/images/*.png")).permitAll()); - super.configure(http); + super.configure(http); setLoginView(http, LoginView.class); } } diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/AboutView.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/AboutView.java index 60c38f9c6..71f562730 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/AboutView.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/AboutView.java @@ -9,7 +9,8 @@ */ package org.eclipse.hawkbit.ui.simple.view; -import org.eclipse.hawkbit.ui.simple.MainLayout; +import jakarta.annotation.security.RolesAllowed; + import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.Image; import com.vaadin.flow.component.orderedlayout.VerticalLayout; @@ -17,12 +18,12 @@ import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import com.vaadin.flow.router.RouteAlias; import com.vaadin.flow.theme.lumo.LumoUtility.Margin; -import jakarta.annotation.security.RolesAllowed; +import org.eclipse.hawkbit.ui.simple.MainLayout; @PageTitle("About") @Route(value = "about", layout = MainLayout.class) @RouteAlias(value = "", layout = MainLayout.class) -@RolesAllowed({"ANONYMOUS"}) +@RolesAllowed({ "ANONYMOUS" }) public class AboutView extends VerticalLayout { public AboutView() { diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/ConfigView.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/ConfigView.java index b8891cf86..277da1849 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/ConfigView.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/ConfigView.java @@ -12,21 +12,21 @@ package org.eclipse.hawkbit.ui.simple.view; import java.util.HashMap; import java.util.Map; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; -import org.eclipse.hawkbit.ui.simple.MainLayout; -import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationValueRequest; +import jakarta.annotation.security.RolesAllowed; +import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Key; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.checkbox.Checkbox; -import com.vaadin.flow.component.Component; import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.component.textfield.NumberField; +import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; -import jakarta.annotation.security.RolesAllowed; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.hawkbit.mgmt.json.model.system.MgmtSystemTenantConfigurationValueRequest; +import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; +import org.eclipse.hawkbit.ui.simple.MainLayout; @PageTitle("Config") @Route(value = "config", layout = MainLayout.class) @@ -34,58 +34,59 @@ import jakarta.annotation.security.RolesAllowed; @Slf4j public class ConfigView extends VerticalLayout { - private static final String WIDTH = "width"; - private static final String PX_300 = "300px"; + private static final String WIDTH = "width"; + private static final String PX_300 = "300px"; - private final Map configValue = new HashMap<>(); + private final Map configValue = new HashMap<>(); - public ConfigView(final HawkbitMgmtClient hawkbitClient) { - setSpacing(false); - final Button saveButton = new Button("Save"); - hawkbitClient.getTenantManagementRestApi().getTenantConfiguration().getBody().forEach((k, v) -> { - Component value = null; - if (v.getValue() instanceof String strValue) { - TextField tf = new TextField(k, strValue, event -> { - final MgmtSystemTenantConfigurationValueRequest vre = new MgmtSystemTenantConfigurationValueRequest(); - vre.setValue(event.getValue()); - configValue.put(k, vre); - }); - tf.getElement().getStyle().set(WIDTH, PX_300); - value = tf; - } else if (v.getValue() instanceof Boolean boolValue) { - value = new Checkbox(k, boolValue, event -> { - final MgmtSystemTenantConfigurationValueRequest vre = new MgmtSystemTenantConfigurationValueRequest(); - vre.setValue(event.getValue()); - configValue.put(k, vre); - }); - } else if (v.getValue() instanceof Long longValue) { - final NumberField nf = new NumberField(k, (double) longValue, event -> { - final MgmtSystemTenantConfigurationValueRequest vre = new MgmtSystemTenantConfigurationValueRequest(); - vre.setValue(event.getValue()); - configValue.put(k, vre); - }); - nf.getElement().getStyle().set(WIDTH, PX_300); - value = nf; - } else if (v.getValue() instanceof Integer intValue) { - final NumberField nf = new NumberField(k, (double) intValue, event -> { - MgmtSystemTenantConfigurationValueRequest vre = new MgmtSystemTenantConfigurationValueRequest(); - vre.setValue(event.getValue()); - configValue.put(k, vre); - }); - nf.getElement().getStyle().set(WIDTH, PX_300); - value = nf; - } else { - log.debug("Unexpected value type: {} -> {} (class: {})", k, v.getValue(), v.getValue() == null ? "null" : v.getValue().getClass()); - } - if (value != null) { - add(value); - } - }); + public ConfigView(final HawkbitMgmtClient hawkbitClient) { + setSpacing(false); + final Button saveButton = new Button("Save"); + hawkbitClient.getTenantManagementRestApi().getTenantConfiguration().getBody().forEach((k, v) -> { + Component value = null; + if (v.getValue() instanceof String strValue) { + TextField tf = new TextField(k, strValue, event -> { + final MgmtSystemTenantConfigurationValueRequest vre = new MgmtSystemTenantConfigurationValueRequest(); + vre.setValue(event.getValue()); + configValue.put(k, vre); + }); + tf.getElement().getStyle().set(WIDTH, PX_300); + value = tf; + } else if (v.getValue() instanceof Boolean boolValue) { + value = new Checkbox(k, boolValue, event -> { + final MgmtSystemTenantConfigurationValueRequest vre = new MgmtSystemTenantConfigurationValueRequest(); + vre.setValue(event.getValue()); + configValue.put(k, vre); + }); + } else if (v.getValue() instanceof Long longValue) { + final NumberField nf = new NumberField(k, (double) longValue, event -> { + final MgmtSystemTenantConfigurationValueRequest vre = new MgmtSystemTenantConfigurationValueRequest(); + vre.setValue(event.getValue()); + configValue.put(k, vre); + }); + nf.getElement().getStyle().set(WIDTH, PX_300); + value = nf; + } else if (v.getValue() instanceof Integer intValue) { + final NumberField nf = new NumberField(k, (double) intValue, event -> { + MgmtSystemTenantConfigurationValueRequest vre = new MgmtSystemTenantConfigurationValueRequest(); + vre.setValue(event.getValue()); + configValue.put(k, vre); + }); + nf.getElement().getStyle().set(WIDTH, PX_300); + value = nf; + } else { + log.debug("Unexpected value type: {} -> {} (class: {})", k, v.getValue(), + v.getValue() == null ? "null" : v.getValue().getClass()); + } + if (value != null) { + add(value); + } + }); - saveButton.addClickListener(click -> - configValue.forEach((key, value) -> - hawkbitClient.getTenantManagementRestApi().updateTenantConfigurationValue(key, value))); - saveButton.addClickShortcut(Key.ENTER); - add(saveButton); - } + saveButton.addClickListener(click -> + configValue.forEach((key, value) -> + hawkbitClient.getTenantManagementRestApi().updateTenantConfigurationValue(key, value))); + saveButton.addClickShortcut(Key.ENTER); + add(saveButton); + } } \ No newline at end of file diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/DistributionSetView.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/DistributionSetView.java index f62fd8610..d7d2fd56a 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/DistributionSetView.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/DistributionSetView.java @@ -9,12 +9,17 @@ */ package org.eclipse.hawkbit.ui.simple.view; -import org.eclipse.hawkbit.ui.simple.MainLayout; -import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; -import org.eclipse.hawkbit.ui.simple.view.util.Filter; -import org.eclipse.hawkbit.ui.simple.view.util.SelectionGrid; -import org.eclipse.hawkbit.ui.simple.view.util.TableView; -import org.eclipse.hawkbit.ui.simple.view.util.Utils; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import jakarta.annotation.security.RolesAllowed; + import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Key; import com.vaadin.flow.component.button.Button; @@ -33,26 +38,22 @@ import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.renderer.ComponentRenderer; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; -import jakarta.annotation.security.RolesAllowed; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPost; import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetType; import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule; import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment; import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag; - -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; +import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; +import org.eclipse.hawkbit.ui.simple.MainLayout; +import org.eclipse.hawkbit.ui.simple.view.util.Filter; +import org.eclipse.hawkbit.ui.simple.view.util.SelectionGrid; +import org.eclipse.hawkbit.ui.simple.view.util.TableView; +import org.eclipse.hawkbit.ui.simple.view.util.Utils; @PageTitle("Distribution Sets") @Route(value = "distribution_sets", layout = MainLayout.class) -@RolesAllowed({"DISTRIBUTION_SET_READ"}) +@RolesAllowed({ "DISTRIBUTION_SET_READ" }) @Uses(Icon.class) public class DistributionSetView extends TableView { @@ -93,6 +94,7 @@ public class DistributionSetView extends TableView { return new SelectionGrid<>( new SelectionGrid.EntityRepresentation<>( MgmtSoftwareModule.class, MgmtSoftwareModule::getModuleId) { + @Override protected void addColumns(Grid grid) { grid.addColumn(MgmtSoftwareModule::getModuleId).setHeader(Constants.ID).setAutoWidth(true); @@ -158,9 +160,9 @@ public class DistributionSetView extends TableView { description.setMinLength(2); Stream.of( - description, - createdBy, createdAt, - lastModifiedBy, lastModifiedAt) + description, + createdBy, createdAt, + lastModifiedBy, lastModifiedAt) .forEach(field -> { field.setReadOnly(true); add(field); @@ -258,17 +260,17 @@ public class DistributionSetView extends TableView { close(); final long distributionSetId = hawkbitClient.getDistributionSetRestApi() .createDistributionSets( - List.of((MgmtDistributionSetRequestBodyPost)new MgmtDistributionSetRequestBodyPost() - .setType(type.getValue().getKey()) - .setName(name.getValue()) - .setVersion(version.getValue()) - .setDescription(description.getValue()) - .setRequiredMigrationStep(requiredMigrationStep.getValue()))) - .getBody() - .stream() - .findFirst() - .orElseThrow() - .getDsId(); + List.of((MgmtDistributionSetRequestBodyPost) new MgmtDistributionSetRequestBodyPost() + .setType(type.getValue().getKey()) + .setName(name.getValue()) + .setVersion(version.getValue()) + .setDescription(description.getValue()) + .setRequiredMigrationStep(requiredMigrationStep.getValue()))) + .getBody() + .stream() + .findFirst() + .orElseThrow() + .getDsId(); new AddSoftwareModulesDialog(distributionSetId, hawkbitClient).open(); }); } @@ -289,17 +291,17 @@ public class DistributionSetView extends TableView { final Component addRemoveControls = Utils.addRemoveControls( v -> new Utils.BaseDialog("Add Software Modules") {{ - final SoftwareModuleView softwareModulesView = new SoftwareModuleView(false, hawkbitClient); - add(softwareModulesView); - final Button addBtn = new Button("Add"); - addBtn.addClickListener(e -> { - softwareModules.addAll(softwareModulesView.getSelection()); - softwareModulesGrid.refreshGrid(false); - close(); - }); - add(addBtn); - open(); - }}.result(), + final SoftwareModuleView softwareModulesView = new SoftwareModuleView(false, hawkbitClient); + add(softwareModulesView); + final Button addBtn = new Button("Add"); + addBtn.addClickListener(e -> { + softwareModules.addAll(softwareModulesView.getSelection()); + softwareModulesGrid.refreshGrid(false); + close(); + }); + add(addBtn); + open(); + }}.result(), v -> { Utils.remove(softwareModulesGrid.getSelectedItems(), softwareModules, MgmtSoftwareModule::getModuleId); softwareModulesGrid.refreshGrid(false); diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/LoginView.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/LoginView.java index 6746a07d5..5aa6c8c31 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/LoginView.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/LoginView.java @@ -9,7 +9,6 @@ */ package org.eclipse.hawkbit.ui.simple.view; -import org.eclipse.hawkbit.ui.simple.security.AuthenticatedUser; import com.vaadin.flow.component.login.LoginI18n; import com.vaadin.flow.component.login.LoginOverlay; import com.vaadin.flow.router.BeforeEnterEvent; @@ -19,6 +18,7 @@ import com.vaadin.flow.router.Route; import com.vaadin.flow.router.internal.RouteUtil; import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.server.auth.AnonymousAllowed; +import org.eclipse.hawkbit.ui.simple.security.AuthenticatedUser; @AnonymousAllowed @PageTitle("Login") diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/RolloutView.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/RolloutView.java index 4e377dd34..e8f8d3097 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/RolloutView.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/RolloutView.java @@ -9,17 +9,20 @@ */ package org.eclipse.hawkbit.ui.simple.view; -import com.vaadin.flow.component.checkbox.Checkbox; -import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; -import org.eclipse.hawkbit.ui.simple.MainLayout; -import org.eclipse.hawkbit.ui.simple.view.util.SelectionGrid; -import org.eclipse.hawkbit.ui.simple.view.util.TableView; -import org.eclipse.hawkbit.ui.simple.view.util.Utils; -import org.eclipse.hawkbit.ui.simple.view.util.Filter; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import jakarta.annotation.security.RolesAllowed; + import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Key; import com.vaadin.flow.component.Text; import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.checkbox.Checkbox; import com.vaadin.flow.component.datetimepicker.DateTimePicker; import com.vaadin.flow.component.dependency.Uses; import com.vaadin.flow.component.formlayout.FormLayout; @@ -37,7 +40,6 @@ import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.renderer.ComponentRenderer; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; -import jakarta.annotation.security.RolesAllowed; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet; import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutCondition; @@ -46,18 +48,17 @@ import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutResponseBody; import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutRestRequestBodyPost; import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroupResponseBody; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; +import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; +import org.eclipse.hawkbit.ui.simple.MainLayout; +import org.eclipse.hawkbit.ui.simple.view.util.Filter; +import org.eclipse.hawkbit.ui.simple.view.util.SelectionGrid; +import org.eclipse.hawkbit.ui.simple.view.util.TableView; +import org.eclipse.hawkbit.ui.simple.view.util.Utils; import org.springframework.util.ObjectUtils; -import java.time.ZoneOffset; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; - @PageTitle("Rollouts") @Route(value = "rollouts", layout = MainLayout.class) -@RolesAllowed({"ROLLOUT_READ"}) +@RolesAllowed({ "ROLLOUT_READ" }) @Uses(Icon.class) public class RolloutView extends TableView { @@ -81,9 +82,9 @@ public class RolloutView extends TableView { grid.addComponentColumn(rollout -> new Actions(rollout, grid, hawkbitClient)).setHeader( Constants.ACTIONS).setAutoWidth(true); - grid.setItemDetailsRenderer(new ComponentRenderer<>( - () -> details, RolloutDetails::setItem)); - } + grid.setItemDetailsRenderer(new ComponentRenderer<>( + () -> details, RolloutDetails::setItem)); + } }, (query, rsqlFilter) -> hawkbitClient.getRolloutRestApi() .getRollouts( @@ -103,6 +104,7 @@ public class RolloutView extends TableView { private static SelectionGrid createGroupGrid() { return new SelectionGrid<>( new SelectionGrid.EntityRepresentation<>(MgmtRolloutGroupResponseBody.class, MgmtRolloutGroupResponseBody::getRolloutGroupId) { + @Override protected void addColumns(final Grid grid) { grid.addColumn(MgmtRolloutGroupResponseBody::getRolloutGroupId).setHeader(Constants.ID).setAutoWidth(true); @@ -120,7 +122,8 @@ public class RolloutView extends TableView { private final Grid grid; private final transient HawkbitMgmtClient hawkbitClient; - private Actions(final MgmtRolloutResponseBody rollout, final Grid grid, final HawkbitMgmtClient hawkbitClient) { + private Actions(final MgmtRolloutResponseBody rollout, final Grid grid, + final HawkbitMgmtClient hawkbitClient) { this.rolloutId = rollout.getRolloutId(); this.grid = grid; this.hawkbitClient = hawkbitClient; @@ -196,7 +199,7 @@ public class RolloutView extends TableView { private final TextField distributionSet = Utils.textField(Constants.DISTRIBUTION_SET); private final TextField actonType = Utils.textField(Constants.ACTION_TYPE); private final TextField startAt = Utils.textField(Constants.START_AT); - private final Checkbox dynamic = new Checkbox(Constants.DYNAMIC); + private final Checkbox dynamic = new Checkbox(Constants.DYNAMIC); private final SelectionGrid groupGrid; private RolloutDetails(final HawkbitMgmtClient hawkbitClient) { @@ -205,11 +208,11 @@ public class RolloutView extends TableView { description.setMinLength(2); groupGrid = createGroupGrid(); Stream.of( - description, - createdBy, createdAt, - lastModifiedBy, lastModifiedAt, - targetFilter, distributionSet, - actonType, startAt) + description, + createdBy, createdAt, + lastModifiedBy, lastModifiedAt, + targetFilter, distributionSet, + actonType, startAt) .forEach(field -> { field.setReadOnly(true); add(field); @@ -258,10 +261,6 @@ public class RolloutView extends TableView { private static class CreateDialog extends Utils.BaseDialog { - private enum StartType { - MANUAL, AUTO, SCHEDULED - } - private final TextField name; private final Select distributionSet; private final Select targetFilter; @@ -274,7 +273,6 @@ public class RolloutView extends TableView { private final NumberField triggerThreshold; private final NumberField errorThreshold; private final Checkbox dynamic = new Checkbox(Constants.DYNAMIC); - private final Button create = new Button("Create"); private CreateDialog(final HawkbitMgmtClient hawkbitClient) { @@ -443,5 +441,9 @@ public class RolloutView extends TableView { hawkbitClient.getRolloutRestApi().create(request).getBody(); }); } + + private enum StartType { + MANUAL, AUTO, SCHEDULED + } } } diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/SoftwareModuleView.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/SoftwareModuleView.java index fc9839fca..7d5877c4f 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/SoftwareModuleView.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/SoftwareModuleView.java @@ -9,12 +9,20 @@ */ package org.eclipse.hawkbit.ui.simple.view; -import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; -import org.eclipse.hawkbit.ui.simple.MainLayout; -import org.eclipse.hawkbit.ui.simple.view.util.SelectionGrid; -import org.eclipse.hawkbit.ui.simple.view.util.TableView; -import org.eclipse.hawkbit.ui.simple.view.util.Utils; -import org.eclipse.hawkbit.ui.simple.view.util.Filter; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import jakarta.annotation.security.RolesAllowed; + import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Key; import com.vaadin.flow.component.button.Button; @@ -35,30 +43,22 @@ import com.vaadin.flow.component.upload.receivers.FileBuffer; import com.vaadin.flow.data.renderer.ComponentRenderer; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; -import jakarta.annotation.security.RolesAllowed; - import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact; import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule; import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleRequestBodyPost; import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType; +import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; +import org.eclipse.hawkbit.ui.simple.MainLayout; +import org.eclipse.hawkbit.ui.simple.view.util.Filter; +import org.eclipse.hawkbit.ui.simple.view.util.SelectionGrid; +import org.eclipse.hawkbit.ui.simple.view.util.TableView; +import org.eclipse.hawkbit.ui.simple.view.util.Utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.multipart.MultipartFile; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; - @PageTitle("Software Modules") @Route(value = "software_modules", layout = MainLayout.class) -@RolesAllowed({"SOFTWARE_MODULE_READ"}) +@RolesAllowed({ "SOFTWARE_MODULE_READ" }) @Uses(Icon.class) public class SoftwareModuleView extends TableView { @@ -73,6 +73,7 @@ public class SoftwareModuleView extends TableView { new SelectionGrid.EntityRepresentation<>(MgmtSoftwareModule.class, MgmtSoftwareModule::getModuleId) { private final SoftwareModuleDetails details = new SoftwareModuleDetails(hawkbitClient); + @Override protected void addColumns(final Grid grid) { grid.addColumn(MgmtSoftwareModule::getModuleId).setHeader(Constants.ID).setAutoWidth(true); @@ -108,6 +109,7 @@ public class SoftwareModuleView extends TableView { private static SelectionGrid createArtifactGrid() { return new SelectionGrid<>( new SelectionGrid.EntityRepresentation<>(MgmtArtifact.class, MgmtArtifact::getArtifactId) { + @Override protected void addColumns(final Grid grid) { grid.addColumn(MgmtArtifact::getArtifactId).setHeader(Constants.ID).setAutoWidth(true); @@ -165,9 +167,9 @@ public class SoftwareModuleView extends TableView { description.setMinLength(2); artifactGrid = createArtifactGrid(); Stream.of( - description, - createdBy, createdAt, - lastModifiedBy, lastModifiedAt) + description, + createdBy, createdAt, + lastModifiedBy, lastModifiedAt) .forEach(field -> { field.setReadOnly(true); add(field); @@ -260,7 +262,7 @@ public class SoftwareModuleView extends TableView { create.addClickListener(e -> { close(); final long softwareModuleId = hawkbitClient.getSoftwareModuleRestApi().createSoftwareModules( - List.of(new MgmtSoftwareModuleRequestBodyPost() + List.of(new MgmtSoftwareModuleRequestBodyPost() .setType(type.getValue().getKey()) .setName(name.getValue()) .setVersion(version.getValue()) @@ -308,7 +310,7 @@ public class SoftwareModuleView extends TableView { }); final Button finishBtn = Utils.tooltip(new Button("Finish"), "Finish (Enter)"); - finishBtn.addClickListener(e -> close()); + finishBtn.addClickListener(e -> close()); finishBtn.addClickShortcut(Key.ENTER); finishBtn.setHeightFull(); final HorizontalLayout finish = new HorizontalLayout(finishBtn); @@ -329,7 +331,7 @@ public class SoftwareModuleView extends TableView { public MultipartFileImpl(final FileBuffer fileBuffer, final long contentLength, final String mimeType) { this.fileBuffer = fileBuffer; - this. contentLength = contentLength; + this.contentLength = contentLength; this.mimeType = mimeType; } diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/TargetView.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/TargetView.java index 8ac13b183..a796a1014 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/TargetView.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/TargetView.java @@ -9,12 +9,17 @@ */ package org.eclipse.hawkbit.ui.simple.view; -import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; -import org.eclipse.hawkbit.ui.simple.view.util.Filter; -import org.eclipse.hawkbit.ui.simple.MainLayout; -import org.eclipse.hawkbit.ui.simple.view.util.SelectionGrid; -import org.eclipse.hawkbit.ui.simple.view.util.TableView; -import org.eclipse.hawkbit.ui.simple.view.util.Utils; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import jakarta.annotation.security.RolesAllowed; + import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Key; import com.vaadin.flow.component.button.Button; @@ -33,27 +38,23 @@ import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.renderer.ComponentRenderer; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; -import jakarta.annotation.security.RolesAllowed; import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQueryRequestBody; import org.eclipse.hawkbit.mgmt.json.model.targettype.MgmtTargetType; +import org.eclipse.hawkbit.ui.simple.HawkbitMgmtClient; +import org.eclipse.hawkbit.ui.simple.MainLayout; +import org.eclipse.hawkbit.ui.simple.view.util.Filter; +import org.eclipse.hawkbit.ui.simple.view.util.SelectionGrid; +import org.eclipse.hawkbit.ui.simple.view.util.TableView; +import org.eclipse.hawkbit.ui.simple.view.util.Utils; import org.springframework.util.ObjectUtils; -import java.util.Collections; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; - @PageTitle("Targets") @Route(value = "targets", layout = MainLayout.class) -@RolesAllowed({"TARGET_READ"}) +@RolesAllowed({ "TARGET_READ" }) @Uses(Icon.class) public class TargetView extends TableView { @@ -156,34 +157,35 @@ public class TargetView extends TableView { .getFilters(0, 30, null, null, null) .getBody().getContent()) .orElse(Collections.emptyList())); - savedFilters.setItemLabelGenerator(query -> Optional.ofNullable(query).map(MgmtTargetFilterQuery::getName).orElse("")); savedFilters.setWidthFull(); textFilter.setWidthFull(); final Button saveBtn = Utils.tooltip(new Button(VaadinIcon.ARCHIVE.create()), "Save (Enter)"); saveBtn.addClickListener(e -> - new Utils.BaseDialog("Save Filter") {{ - setHeight("40%"); - final Button finishBtn = Utils.tooltip(new Button("Save"), "Save (Enter)"); - final TextField name = Utils.textField( - Constants.NAME, - e -> finishBtn.setEnabled(!e.getHasValue().isEmpty())); - name.focus(); - finishBtn.addClickShortcut(Key.ENTER); - finishBtn.setEnabled(false); - finishBtn.addClickListener(e -> { - final MgmtTargetFilterQueryRequestBody createRequest = new MgmtTargetFilterQueryRequestBody(); - createRequest.setName(name.getValue()); - createRequest.setQuery(textFilter.getValue()); - hawkbitClient.getTargetFilterQueryRestApi().createFilter(createRequest); - savedFilters.setItems( - hawkbitClient.getTargetFilterQueryRestApi() - .getFilters(0, 30, null, null, null).getBody().getContent()); - close(); - }); - add(name, finishBtn); - open(); - }}); + new Utils.BaseDialog("Save Filter") {{ + setHeight("40%"); + final Button finishBtn = Utils.tooltip(new Button("Save"), "Save (Enter)"); + final TextField name = Utils.textField( + Constants.NAME, + e -> finishBtn.setEnabled(!e.getHasValue().isEmpty())); + name.focus(); + finishBtn.addClickShortcut(Key.ENTER); + finishBtn.setEnabled(false); + finishBtn.addClickListener(e -> { + final MgmtTargetFilterQueryRequestBody createRequest = new MgmtTargetFilterQueryRequestBody(); + createRequest.setName(name.getValue()); + createRequest.setQuery(textFilter.getValue()); + hawkbitClient.getTargetFilterQueryRestApi().createFilter(createRequest); + savedFilters.setItems( + hawkbitClient.getTargetFilterQueryRestApi() + .getFilters(0, 30, null, null, null).getBody().getContent()); + close(); + }); + add(name, finishBtn); + open(); + }}); saveBtn.addClickShortcut(Key.ENTER); layout.setSpacing(false); @@ -216,10 +218,10 @@ public class TargetView extends TableView { private TargetDetails() { description.setMinLength(2); Stream.of( - description, - createdBy, createdAt, - lastModifiedBy, lastModifiedAt, - securityToken) + description, + createdBy, createdAt, + lastModifiedBy, lastModifiedAt, + securityToken) .forEach(field -> { field.setReadOnly(true); add(field); @@ -300,7 +302,7 @@ public class TargetView extends TableView { request.setTargetType(type.getValue().getTypeId()); } hawkbitClient.getTargetRestApi().createTargets( - List.of(request)) + List.of(request)) .getBody() .stream() .findFirst() diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/Filter.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/Filter.java index 0a47611b4..ed75b13b0 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/Filter.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/Filter.java @@ -9,6 +9,15 @@ */ package org.eclipse.hawkbit.ui.simple.view.util; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import com.vaadin.flow.component.Component; import com.vaadin.flow.component.HasValue; import com.vaadin.flow.component.button.Button; @@ -19,15 +28,6 @@ import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.theme.lumo.LumoUtility; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; - public class Filter extends Div { private transient Rsql rsql; @@ -46,7 +46,7 @@ public class Filter extends Div { filtersDiv.add(primaryRsql.components()); filtersDiv.addClassName(LumoUtility.Gap.SMALL); - final Button searchBtn = Utils.tooltip(new Button(VaadinIcon.REFRESH.create()),"Search"); + final Button searchBtn = Utils.tooltip(new Button(VaadinIcon.REFRESH.create()), "Search"); searchBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY); searchBtn.addClickListener(e -> changeListener.accept(rsql.filter())); final Button resetBtn = Utils.tooltip(new Button(VaadinIcon.ERASER.create()), "Reset"); diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/SelectionGrid.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/SelectionGrid.java index a232b9f40..0ef4f538f 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/SelectionGrid.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/SelectionGrid.java @@ -9,11 +9,6 @@ */ package org.eclipse.hawkbit.ui.simple.view.util; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.grid.GridVariant; -import com.vaadin.flow.data.provider.Query; -import com.vaadin.flow.theme.lumo.LumoUtility; - import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -21,12 +16,16 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Stream; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.grid.GridVariant; +import com.vaadin.flow.data.provider.Query; +import com.vaadin.flow.theme.lumo.LumoUtility; + // id type shall have proper equals and hashCode - i.e. eligible hash set element -public class SelectionGrid extends Grid { +public class SelectionGrid extends Grid { private volatile String rsqlFilter; - public SelectionGrid( final EntityRepresentation entityRepresentation) { this(entityRepresentation, null); diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/TableView.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/TableView.java index f5d923d99..ec9876bda 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/TableView.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/TableView.java @@ -9,16 +9,16 @@ */ package org.eclipse.hawkbit.ui.simple.view.util; -import org.eclipse.hawkbit.ui.simple.view.Constants; -import com.vaadin.flow.component.html.Div; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.data.provider.Query; - import java.util.concurrent.CompletionStage; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Stream; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.data.provider.Query; +import org.eclipse.hawkbit.ui.simple.view.Constants; + public class TableView extends Div implements Constants { protected SelectionGrid selectionGrid; @@ -30,12 +30,14 @@ public class TableView extends Div implements Constants { final BiFunction, String, Stream> queryFn) { this(rsql, null, entityRepresentation, queryFn, null, null); } + public TableView( final Filter.Rsql rsql, final Filter.Rsql alternativeRsql, final SelectionGrid.EntityRepresentation entityRepresentation, final BiFunction, String, Stream> queryFn) { - this(rsql, alternativeRsql, entityRepresentation, queryFn,null, null); + this(rsql, alternativeRsql, entityRepresentation, queryFn, null, null); } + public TableView( final Filter.Rsql rsql, final SelectionGrid.EntityRepresentation entityRepresentation, @@ -44,6 +46,7 @@ public class TableView extends Div implements Constants { final Function, CompletionStage> removeHandler) { this(rsql, null, entityRepresentation, queryFn, addHandler, removeHandler); } + public TableView( final Filter.Rsql rsql, final Filter.Rsql alternativeRsql, final SelectionGrid.EntityRepresentation entityRepresentation, @@ -55,7 +58,7 @@ public class TableView extends Div implements Constants { setSizeFull(); - final VerticalLayout layout = new VerticalLayout(filter, selectionGrid); + final VerticalLayout layout = new VerticalLayout(filter, selectionGrid); layout.setSizeFull(); layout.setPadding(false); layout.setSpacing(false); diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/Utils.java b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/Utils.java index 449090dae..a9d9d6bd4 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/Utils.java +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/Utils.java @@ -9,6 +9,14 @@ */ package org.eclipse.hawkbit.ui.simple.view.util; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Consumer; +import java.util.function.Function; + import com.vaadin.flow.component.Component; import com.vaadin.flow.component.HasValue; import com.vaadin.flow.component.Text; @@ -28,14 +36,6 @@ import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.value.ValueChangeMode; import com.vaadin.flow.theme.lumo.LumoUtility; -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.function.Consumer; -import java.util.function.Function; - public class Utils { private Utils() { @@ -45,6 +45,7 @@ public class Utils { public static TextField textField(final String label) { return textField(label, null); } + public static TextField textField(final String label, final Consumer> changeListener) { final TextField textField = new TextField(label); textField.setWidthFull(); @@ -60,6 +61,7 @@ public class Utils { public static NumberField numberField(final String label) { return numberField(label, null); } + public static NumberField numberField(final String label, final Consumer> changeListener) { final NumberField numberField = new NumberField(label); numberField.setWidthFull(); @@ -109,7 +111,7 @@ public class Utils { public static void remove(final Collection remove, final Set from, final Function idFn) { remove.forEach(toRemove -> { final Object id = idFn.apply(toRemove); - for (final Iterator i = from.iterator(); i.hasNext();) { + for (final Iterator i = from.iterator(); i.hasNext(); ) { if (idFn.apply(i.next()).equals(id)) { i.remove(); } diff --git a/hawkbit-runtime/hawkbit-simple-ui/src/main/resources/application.properties b/hawkbit-runtime/hawkbit-simple-ui/src/main/resources/application.properties index 6e405e4a2..4485de798 100644 --- a/hawkbit-runtime/hawkbit-simple-ui/src/main/resources/application.properties +++ b/hawkbit-runtime/hawkbit-simple-ui/src/main/resources/application.properties @@ -19,11 +19,11 @@ logging.pattern.console=%clr(%d{${logging.pattern.dateformat:yyyy-MM-dd'T'HH:mm: ### Vaadin start ###` # build with mvn vaadin:build-frontend to enable / disable vaadin.frontend.hotdeploy=false -logging.level.org.atmosphere = warn -spring.mustache.check-template-location = false +logging.level.org.atmosphere=warn +spring.mustache.check-template-location=false # Launch the default browser when starting the application in development mode vaadin.launch-browser=true # To improve the performance during development. # For more information https://vaadin.com/docs/flow/spring/tutorial-spring-configuration.html#special-configuration-parameters -vaadin.whitelisted-packages = com.vaadin,org.vaadin,dev.hilla,org.eclipse.hawkbit +vaadin.whitelisted-packages=com.vaadin,org.vaadin,dev.hilla,org.eclipse.hawkbit ### Vaadin end ### \ No newline at end of file diff --git a/hawkbit-runtime/hawkbit-update-server/README.md b/hawkbit-runtime/hawkbit-update-server/README.md index 12453a1f5..a40ed8fca 100644 --- a/hawkbit-runtime/hawkbit-update-server/README.md +++ b/hawkbit-runtime/hawkbit-update-server/README.md @@ -41,7 +41,8 @@ Add to your `pom.xml` : ``` -Optional as well is the addition of [Protostuff](https://github.com/protostuff/protostuff) based message payload serialization for improved performance. +Optional as well is the addition of [Protostuff](https://github.com/protostuff/protostuff) based message payload +serialization for improved performance. Add to your `application.properties` : diff --git a/hawkbit-runtime/hawkbit-update-server/pom.xml b/hawkbit-runtime/hawkbit-update-server/pom.xml index b6937dbc6..263b005d3 100644 --- a/hawkbit-runtime/hawkbit-update-server/pom.xml +++ b/hawkbit-runtime/hawkbit-update-server/pom.xml @@ -9,7 +9,7 @@ SPDX-License-Identifier: EPL-2.0 --> - 4.0.0 diff --git a/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java b/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java index 9cc65767a..def878ee8 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java +++ b/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java @@ -36,11 +36,9 @@ public class ErrorController extends BasicErrorController { /** * A new {@link ErrorController}. - * - * @param errorAttributes - * the error attributes - * @param serverProperties - * configuration properties + * + * @param errorAttributes the error attributes + * @param serverProperties configuration properties */ public ErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) { super(errorAttributes, serverProperties.getError()); diff --git a/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/Start.java b/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/Start.java index 8290d3ae8..a93b67064 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/Start.java +++ b/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/Start.java @@ -22,7 +22,6 @@ import org.springframework.web.servlet.view.RedirectView; /** * A {@link SpringBootApplication} annotated class with a main method to start. * The minimal configuration for the stand alone hawkBit server. - * */ @SpringBootApplication(scanBasePackages = "org.eclipse.hawkbit") @EnableHawkbitManagedSecurityConfiguration diff --git a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AbstractSecurityTest.java b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AbstractSecurityTest.java index fc1355a75..b126fb5bd 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AbstractSecurityTest.java +++ b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AbstractSecurityTest.java @@ -24,11 +24,10 @@ import org.springframework.web.context.WebApplicationContext; @ExtendWith(SharedSqlTestDatabaseExtension.class) public abstract class AbstractSecurityTest { + protected MockMvc mvc; @Autowired private WebApplicationContext context; - protected MockMvc mvc; - @BeforeEach public void setup() { final DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(context) diff --git a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AllowedHostNamesTest.java b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AllowedHostNamesTest.java index 9abb05cdf..8d20bd49e 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AllowedHostNamesTest.java +++ b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/AllowedHostNamesTest.java @@ -9,17 +9,14 @@ */ package org.eclipse.hawkbit.app; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import io.qameta.allure.Description; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpHeaders; -import org.springframework.security.web.firewall.RequestRejectedException; - import io.qameta.allure.Feature; import io.qameta.allure.Story; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; import org.springframework.test.context.TestPropertySource; @TestPropertySource(properties = { "hawkbit.server.security.allowedHostNames=localhost", diff --git a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java index c9d5e5413..cce98e2a8 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java +++ b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java @@ -14,19 +14,17 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import io.qameta.allure.Description; +import io.qameta.allure.Feature; +import io.qameta.allure.Story; import org.eclipse.hawkbit.im.authentication.SpRole; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; - import org.eclipse.hawkbit.repository.test.util.WithUser; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpHeaders; import org.springframework.test.web.servlet.ResultActions; -import io.qameta.allure.Description; -import io.qameta.allure.Feature; -import io.qameta.allure.Story; - @SpringBootTest( properties = { "hawkbit.dmf.rabbitmq.enabled=false", @@ -35,7 +33,7 @@ import io.qameta.allure.Story; CorsTest.ALLOWED_ORIGIN_FIRST + "," + CorsTest.ALLOWED_ORIGIN_SECOND, "hawkbit.server.security.cors.exposedHeaders=" + - HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN}) + HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN }) @Feature("Integration Test - Security") @Story("CORS") public class CorsTest extends AbstractSecurityTest { @@ -63,8 +61,8 @@ public class CorsTest extends AbstractSecurityTest { final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin( MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden()) - .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn() - .getResponse().getContentAsString(); + .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).andReturn() + .getResponse().getContentAsString(); assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST); } diff --git a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/PreAuthorizeEnabledTest.java b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/PreAuthorizeEnabledTest.java index 8a9316096..f5c2100a3 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/PreAuthorizeEnabledTest.java +++ b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/PreAuthorizeEnabledTest.java @@ -9,6 +9,11 @@ */ package org.eclipse.hawkbit.app; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + +import java.util.HashMap; + import com.fasterxml.jackson.databind.ObjectMapper; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -19,26 +24,21 @@ import org.eclipse.hawkbit.repository.test.util.WithUser; import org.junit.jupiter.api.Test; import org.springframework.http.HttpStatus; -import java.util.HashMap; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; - @Feature("Integration Test - Security") @Story("PreAuthorized enabled") public class PreAuthorizeEnabledTest extends AbstractSecurityTest { @Test @Description("Tests whether request fail if a role is forbidden for the user") - @WithUser(authorities = { SpPermission.READ_TARGET } ) + @WithUser(authorities = { SpPermission.READ_TARGET }) public void failIfNoRole() throws Exception { mvc.perform(get("/rest/v1/distributionsets")).andExpect(result -> - assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value())); + assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.FORBIDDEN.value())); } @Test @Description("Tests whether request succeed if a role is granted for the user") - @WithUser(authorities = { SpPermission.READ_REPOSITORY }) + @WithUser(authorities = { SpPermission.READ_REPOSITORY }) public void successIfHasRole() throws Exception { mvc.perform(get("/rest/v1/distributionsets")).andExpect(result -> { assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()); @@ -47,7 +47,7 @@ public class PreAuthorizeEnabledTest extends AbstractSecurityTest { @Test @Description("Tests whether request succeed if a role is granted for the user") - @WithUser(authorities = { SpRole.TENANT_ADMIN }) + @WithUser(authorities = { SpRole.TENANT_ADMIN }) public void successIfHasTenantAdminRole() throws Exception { mvc.perform(get("/rest/v1/distributionsets")).andExpect(result -> { assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value()); @@ -57,7 +57,7 @@ public class PreAuthorizeEnabledTest extends AbstractSecurityTest { @Test @Description("Tests whether read tenant config request fail if a tenant config (or read read) is not " + "granted for the user") - @WithUser(authorities = { SpPermission.READ_TARGET } ) + @WithUser(authorities = { SpPermission.READ_TARGET }) public void onlyDSIfNoTenantConfig() throws Exception { mvc.perform(get("/rest/v1/system/configs")).andExpect(result -> { // returns default DS type because of READ_TARGET @@ -72,7 +72,7 @@ public class PreAuthorizeEnabledTest extends AbstractSecurityTest { @Test @Description("Tests whether read tenant config request succeed if a tenant config (not read explicitly) is " + "granted for the user") - @WithUser(authorities = { SpPermission.TENANT_CONFIGURATION }) + @WithUser(authorities = { SpPermission.TENANT_CONFIGURATION }) public void successIfHasTenantConfig() throws Exception { mvc.perform(get("/rest/v1/system/configs")).andExpect(result -> assertThat(result.getResponse().getStatus()).isEqualTo(HttpStatus.OK.value())); diff --git a/hawkbit-runtime/pom.xml b/hawkbit-runtime/pom.xml index 16c7aebad..2379abb61 100644 --- a/hawkbit-runtime/pom.xml +++ b/hawkbit-runtime/pom.xml @@ -9,96 +9,97 @@ SPDX-License-Identifier: EPL-2.0 --> - - 4.0.0 + + 4.0.0 - - org.eclipse.hawkbit - hawkbit-parent - ${revision} - + + org.eclipse.hawkbit + hawkbit-parent + ${revision} + - hawkbit-runtime-parent - hawkBit :: Runtime :: Parent - pom + hawkbit-runtime-parent + hawkBit :: Runtime :: Parent + pom - - - - com.h2database - h2 - - - com.microsoft.sqlserver - mssql-jdbc - - - org.postgresql - postgresql - + + + + com.h2database + h2 + + + com.microsoft.sqlserver + mssql-jdbc + + + org.postgresql + postgresql + - - - io.qameta.allure - allure-junit5 - test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.security - spring-security-test - test - - - org.mariadb.jdbc - mariadb-java-client - test - - - org.eclipse.hawkbit - hawkbit-repository-test - ${project.version} - test - - - - - - + + + io.qameta.allure + allure-junit5 + test + + org.springframework.boot - spring-boot-maven-plugin - - - - repackage - - - ${baseDir} - ${spring.app.class} - JAR - - - - - + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + org.mariadb.jdbc + mariadb-java-client + test + + + org.eclipse.hawkbit + hawkbit-repository-test + ${project.version} + test + + - - - src/main/resources - - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + ${baseDir} + ${spring.app.class} + JAR + + + + + - - hawkbit-ddi-server - hawkbit-dmf-server - hawkbit-mgmt-server - hawkbit-simple-ui + + + src/main/resources + + + - hawkbit-update-server - + + hawkbit-ddi-server + hawkbit-dmf-server + hawkbit-mgmt-server + hawkbit-simple-ui + + hawkbit-update-server +