Code format hawkbit-runtime (#1947)
Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -3,10 +3,10 @@ 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 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**
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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_
|
||||
|
||||
Note that here you have to use your maven repository containing the hawkBit app as docker build context, in the example
|
||||
case _~/.m2/repository_
|
||||
@@ -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!
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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!
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
|
||||
@@ -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!
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
|
||||
@@ -37,10 +37,8 @@ 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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,9 @@
|
||||
SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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(""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> 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<String> 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
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<String, MgmtSystemTenantConfigurationValueRequest> configValue = new HashMap<>();
|
||||
private final Map<String, MgmtSystemTenantConfigurationValueRequest> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<MgmtDistributionSet, Long> {
|
||||
|
||||
@@ -93,6 +94,7 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
return new SelectionGrid<>(
|
||||
new SelectionGrid.EntityRepresentation<>(
|
||||
MgmtSoftwareModule.class, MgmtSoftwareModule::getModuleId) {
|
||||
|
||||
@Override
|
||||
protected void addColumns(Grid<MgmtSoftwareModule> grid) {
|
||||
grid.addColumn(MgmtSoftwareModule::getModuleId).setHeader(Constants.ID).setAutoWidth(true);
|
||||
@@ -158,9 +160,9 @@ public class DistributionSetView extends TableView<MgmtDistributionSet, Long> {
|
||||
|
||||
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<MgmtDistributionSet, Long> {
|
||||
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<MgmtDistributionSet, Long> {
|
||||
|
||||
final Component addRemoveControls = Utils.addRemoveControls(
|
||||
v -> new Utils.BaseDialog<Void>("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);
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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<MgmtRolloutResponseBody, Long> {
|
||||
|
||||
@@ -81,9 +82,9 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
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<MgmtRolloutResponseBody, Long> {
|
||||
private static SelectionGrid<MgmtRolloutGroupResponseBody, Long> createGroupGrid() {
|
||||
return new SelectionGrid<>(
|
||||
new SelectionGrid.EntityRepresentation<>(MgmtRolloutGroupResponseBody.class, MgmtRolloutGroupResponseBody::getRolloutGroupId) {
|
||||
|
||||
@Override
|
||||
protected void addColumns(final Grid<MgmtRolloutGroupResponseBody> grid) {
|
||||
grid.addColumn(MgmtRolloutGroupResponseBody::getRolloutGroupId).setHeader(Constants.ID).setAutoWidth(true);
|
||||
@@ -120,7 +122,8 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
private final Grid<MgmtRolloutResponseBody> grid;
|
||||
private final transient HawkbitMgmtClient hawkbitClient;
|
||||
|
||||
private Actions(final MgmtRolloutResponseBody rollout, final Grid<MgmtRolloutResponseBody> grid, final HawkbitMgmtClient hawkbitClient) {
|
||||
private Actions(final MgmtRolloutResponseBody rollout, final Grid<MgmtRolloutResponseBody> grid,
|
||||
final HawkbitMgmtClient hawkbitClient) {
|
||||
this.rolloutId = rollout.getRolloutId();
|
||||
this.grid = grid;
|
||||
this.hawkbitClient = hawkbitClient;
|
||||
@@ -196,7 +199,7 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
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<MgmtRolloutGroupResponseBody, Long> groupGrid;
|
||||
|
||||
private RolloutDetails(final HawkbitMgmtClient hawkbitClient) {
|
||||
@@ -205,11 +208,11 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
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<MgmtRolloutResponseBody, Long> {
|
||||
|
||||
private static class CreateDialog extends Utils.BaseDialog<Void> {
|
||||
|
||||
private enum StartType {
|
||||
MANUAL, AUTO, SCHEDULED
|
||||
}
|
||||
|
||||
private final TextField name;
|
||||
private final Select<MgmtDistributionSet> distributionSet;
|
||||
private final Select<MgmtTargetFilterQuery> targetFilter;
|
||||
@@ -274,7 +273,6 @@ public class RolloutView extends TableView<MgmtRolloutResponseBody, Long> {
|
||||
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<MgmtRolloutResponseBody, Long> {
|
||||
hawkbitClient.getRolloutRestApi().create(request).getBody();
|
||||
});
|
||||
}
|
||||
|
||||
private enum StartType {
|
||||
MANUAL, AUTO, SCHEDULED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<MgmtSoftwareModule, Long> {
|
||||
|
||||
@@ -73,6 +73,7 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
new SelectionGrid.EntityRepresentation<>(MgmtSoftwareModule.class, MgmtSoftwareModule::getModuleId) {
|
||||
|
||||
private final SoftwareModuleDetails details = new SoftwareModuleDetails(hawkbitClient);
|
||||
|
||||
@Override
|
||||
protected void addColumns(final Grid<MgmtSoftwareModule> grid) {
|
||||
grid.addColumn(MgmtSoftwareModule::getModuleId).setHeader(Constants.ID).setAutoWidth(true);
|
||||
@@ -108,6 +109,7 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
private static SelectionGrid<MgmtArtifact, Long> createArtifactGrid() {
|
||||
return new SelectionGrid<>(
|
||||
new SelectionGrid.EntityRepresentation<>(MgmtArtifact.class, MgmtArtifact::getArtifactId) {
|
||||
|
||||
@Override
|
||||
protected void addColumns(final Grid<MgmtArtifact> grid) {
|
||||
grid.addColumn(MgmtArtifact::getArtifactId).setHeader(Constants.ID).setAutoWidth(true);
|
||||
@@ -165,9 +167,9 @@ public class SoftwareModuleView extends TableView<MgmtSoftwareModule, Long> {
|
||||
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<MgmtSoftwareModule, Long> {
|
||||
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<MgmtSoftwareModule, Long> {
|
||||
});
|
||||
|
||||
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<MgmtSoftwareModule, Long> {
|
||||
|
||||
public MultipartFileImpl(final FileBuffer fileBuffer, final long contentLength, final String mimeType) {
|
||||
this.fileBuffer = fileBuffer;
|
||||
this. contentLength = contentLength;
|
||||
this.contentLength = contentLength;
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<MgmtTarget, String> {
|
||||
|
||||
@@ -156,34 +157,35 @@ public class TargetView extends TableView<MgmtTarget, String> {
|
||||
.getFilters(0, 30, null, null, null)
|
||||
.getBody().getContent())
|
||||
.orElse(Collections.emptyList()));
|
||||
savedFilters.setItemLabelGenerator(query -> Optional.ofNullable(query).map(MgmtTargetFilterQuery::getName).orElse("<select saved filter>"));
|
||||
savedFilters.setItemLabelGenerator(
|
||||
query -> Optional.ofNullable(query).map(MgmtTargetFilterQuery::getName).orElse("<select saved filter>"));
|
||||
savedFilters.setWidthFull();
|
||||
|
||||
textFilter.setWidthFull();
|
||||
final Button saveBtn = Utils.tooltip(new Button(VaadinIcon.ARCHIVE.create()), "Save (Enter)");
|
||||
saveBtn.addClickListener(e ->
|
||||
new Utils.BaseDialog<Void>("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<Void>("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<MgmtTarget, String> {
|
||||
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<MgmtTarget, String> {
|
||||
request.setTargetType(type.getValue().getTypeId());
|
||||
}
|
||||
hawkbitClient.getTargetRestApi().createTargets(
|
||||
List.of(request))
|
||||
List.of(request))
|
||||
.getBody()
|
||||
.stream()
|
||||
.findFirst()
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<T,ID> extends Grid<T> {
|
||||
public class SelectionGrid<T, ID> extends Grid<T> {
|
||||
|
||||
private volatile String rsqlFilter;
|
||||
|
||||
|
||||
public SelectionGrid(
|
||||
final EntityRepresentation<T, ID> entityRepresentation) {
|
||||
this(entityRepresentation, null);
|
||||
|
||||
@@ -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<T, ID> extends Div implements Constants {
|
||||
|
||||
protected SelectionGrid<T, ID> selectionGrid;
|
||||
@@ -30,12 +30,14 @@ public class TableView<T, ID> extends Div implements Constants {
|
||||
final BiFunction<Query<T, Void>, String, Stream<T>> queryFn) {
|
||||
this(rsql, null, entityRepresentation, queryFn, null, null);
|
||||
}
|
||||
|
||||
public TableView(
|
||||
final Filter.Rsql rsql, final Filter.Rsql alternativeRsql,
|
||||
final SelectionGrid.EntityRepresentation<T, ID> entityRepresentation,
|
||||
final BiFunction<Query<T, Void>, String, Stream<T>> queryFn) {
|
||||
this(rsql, alternativeRsql, entityRepresentation, queryFn,null, null);
|
||||
this(rsql, alternativeRsql, entityRepresentation, queryFn, null, null);
|
||||
}
|
||||
|
||||
public TableView(
|
||||
final Filter.Rsql rsql,
|
||||
final SelectionGrid.EntityRepresentation<T, ID> entityRepresentation,
|
||||
@@ -44,6 +46,7 @@ public class TableView<T, ID> extends Div implements Constants {
|
||||
final Function<SelectionGrid<T, ID>, CompletionStage<Void>> removeHandler) {
|
||||
this(rsql, null, entityRepresentation, queryFn, addHandler, removeHandler);
|
||||
}
|
||||
|
||||
public TableView(
|
||||
final Filter.Rsql rsql, final Filter.Rsql alternativeRsql,
|
||||
final SelectionGrid.EntityRepresentation<T, ID> entityRepresentation,
|
||||
@@ -55,7 +58,7 @@ public class TableView<T, ID> 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);
|
||||
|
||||
@@ -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<HasValue.ValueChangeEvent<String>> 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<HasValue.ValueChangeEvent<Double>> changeListener) {
|
||||
final NumberField numberField = new NumberField(label);
|
||||
numberField.setWidthFull();
|
||||
@@ -109,7 +111,7 @@ public class Utils {
|
||||
public static <T> void remove(final Collection<T> remove, final Set<T> from, final Function<T, ?> idFn) {
|
||||
remove.forEach(toRemove -> {
|
||||
final Object id = idFn.apply(toRemove);
|
||||
for (final Iterator<T> i = from.iterator(); i.hasNext();) {
|
||||
for (final Iterator<T> i = from.iterator(); i.hasNext(); ) {
|
||||
if (idFn.apply(i.next()).equals(id)) {
|
||||
i.remove();
|
||||
}
|
||||
|
||||
@@ -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 ###
|
||||
@@ -41,7 +41,8 @@ Add to your `pom.xml` :
|
||||
</dependency>
|
||||
```
|
||||
|
||||
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` :
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
|
||||
@@ -37,10 +37,8 @@ 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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -9,96 +9,97 @@
|
||||
SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<parent>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-parent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hawkbit-runtime-parent</artifactId>
|
||||
<name>hawkBit :: Runtime :: Parent</name>
|
||||
<packaging>pom</packaging>
|
||||
<artifactId>hawkbit-runtime-parent</artifactId>
|
||||
<name>hawkBit :: Runtime :: Parent</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<!-- Common runtime dependences -->
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<!-- Common runtime dependences -->
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>io.qameta.allure</groupId>
|
||||
<artifactId>allure-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>io.qameta.allure</groupId>
|
||||
<artifactId>allure-junit5</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${baseDir}</outputDirectory>
|
||||
<mainClass>${spring.app.class}</mainClass>
|
||||
<layout>JAR</layout>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-repository-test</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${baseDir}</outputDirectory>
|
||||
<mainClass>${spring.app.class}</mainClass>
|
||||
<layout>JAR</layout>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<modules>
|
||||
<module>hawkbit-ddi-server</module>
|
||||
<module>hawkbit-dmf-server</module>
|
||||
<module>hawkbit-mgmt-server</module>
|
||||
<module>hawkbit-simple-ui</module>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<module>hawkbit-update-server</module>
|
||||
</modules>
|
||||
<modules>
|
||||
<module>hawkbit-ddi-server</module>
|
||||
<module>hawkbit-dmf-server</module>
|
||||
<module>hawkbit-mgmt-server</module>
|
||||
<module>hawkbit-simple-ui</module>
|
||||
|
||||
<module>hawkbit-update-server</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
||||
Reference in New Issue
Block a user