diff --git a/Jenkinsfile b/Jenkinsfile index 19254ba..b45541a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,8 +3,8 @@ pipeline { environment { HARBOR_HOST = "harbor.dinauer.dev" - PROJECT = "metrics-service" - REPO = "backend" + PROJECT = "big-bucket" + REPO = "core" } stages { diff --git a/bruno/Metrics Service/Get.bru b/bruno/Metrics Service/Get.bru index 0205e6b..5bb14de 100644 --- a/bruno/Metrics Service/Get.bru +++ b/bruno/Metrics Service/Get.bru @@ -5,18 +5,17 @@ meta { } get { - url: http://localhost:8080/api/metrics/POD-545444456/WORKLOAD?bucket-unit=TOTAL + url: http://localhost:8080/api/metrics/POD-2/WORKLOAD?bucket-unit=TOTAL body: none - auth: basic + auth: bearer } params:query { bucket-unit: TOTAL } -auth:basic { - username: kubooboo - password: 3749832748923748923 +auth:bearer { + token: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1cG4iOiJiMzk2ODFiMy0zMjNjLTQ3ZDctYjdmYS04MjM4NDc2MjlhZWMiLCJleHAiOjE3NjM5MzI4MjksImdyb3VwcyI6WyJBRE1JTiJdLCJpYXQiOjE3NjI2MzY4MjksImp0aSI6IjczY2UyZjQ2LTUwY2YtNGIyMS04MDMwLTRhY2YyNjQ3MzhiYSIsImlzcyI6Imh0dHBzOi8vcXVhcmt1cy5pby9pc3N1ZXIifQ.ihikbyPyg8Km2JYa6T0OW5O9fEaEHRcubJHBhbzLp5f068wA2PFqjJ5iJ81-v946OwahAZxv2zoiElt6F-e5kLGhnMAlZu55B1l7VuGQxNCsjw9CWE3d74LEcytzCbtzgs5hIR2111aP0YFMLgsCc8cvPBdXlEXDZaOCznPBW--dLDooNsrwmeA4j4kCYaFn2mcBfplIifNjxxwRajhRmXGKRw5_U1YMa-gjQ1O5vXcvlhijxPHT3C-BeR4Y8QEvjWmX-844K-ga1pt98eDU4Es51Lw5KVCkOJhgeibS0CllzZf0h95keTYCEseSFST-ldPDcBAUpsmwrcrDmTJPCQ } settings { diff --git a/bruno/Metrics Service/Insert.bru b/bruno/Metrics Service/Insert.bru index d489ac5..4516feb 100644 --- a/bruno/Metrics Service/Insert.bru +++ b/bruno/Metrics Service/Insert.bru @@ -5,7 +5,7 @@ meta { } post { - url: http://localhost:8080/api/metrics/POD-545444456/WORKLOAD + url: http://localhost:8080/api/metrics/POD-2/WORKLOAD body: json auth: basic } @@ -15,15 +15,18 @@ headers { } auth:basic { - username: kubooboo - password: 3749832748923748923 + username: michaeltheduck + password: blabla } body:json { { - "RELATIVE_CPU": "20", - "RELATIVE_MEMORY": "23", - "RELATIVE_DISK_USAGE": "56" + "owner": "kubooboo", + "values": { + "RELATIVE_CPU": "20", + "RELATIVE_MEMORY": "23", + "RELATIVE_DISK_USAGE": "56" + } } } diff --git a/pom.xml b/pom.xml index a86fb86..f48772c 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 dev.dinauer - metrics-service + big-bucket 1.0.0-SNAPSHOT diff --git a/src/main/java/dev/dinauer/metrics/service/Insert.java b/src/main/java/dev/dinauer/metrics/service/Insert.java new file mode 100644 index 0000000..7adee85 --- /dev/null +++ b/src/main/java/dev/dinauer/metrics/service/Insert.java @@ -0,0 +1,9 @@ +package dev.dinauer.metrics.service; + +import java.util.Map; + +import jakarta.validation.constraints.Size; + +public record Insert(String owner, @Size(min = 1) Map values) +{ +} \ No newline at end of file diff --git a/src/main/java/dev/dinauer/metrics/service/Resource.java b/src/main/java/dev/dinauer/metrics/service/Resource.java index 2380725..4c769b1 100644 --- a/src/main/java/dev/dinauer/metrics/service/Resource.java +++ b/src/main/java/dev/dinauer/metrics/service/Resource.java @@ -1,7 +1,6 @@ package dev.dinauer.metrics.service; import java.util.List; -import java.util.Map; import java.util.Optional; import jakarta.inject.Inject; @@ -21,11 +20,11 @@ public class Resource Service service; @POST - public void insert(@PathParam("resource") String resource, @PathParam("metric") String metric, Map values) + public void insert(@PathParam("resource") String resource, @PathParam("metric") String metric, Insert insert) { if (authenticationService.canWrite()) { - service.index(resource, metric, values); + service.index(resource, metric, insert.owner(), insert.values()); } else { diff --git a/src/main/java/dev/dinauer/metrics/service/Service.java b/src/main/java/dev/dinauer/metrics/service/Service.java index 57741a5..ef15481 100644 --- a/src/main/java/dev/dinauer/metrics/service/Service.java +++ b/src/main/java/dev/dinauer/metrics/service/Service.java @@ -4,10 +4,14 @@ import java.time.Clock; import java.time.ZonedDateTime; import java.util.List; import java.util.Map; +import java.util.Objects; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; +import jakarta.ws.rs.ForbiddenException; + +import org.jboss.logging.Logger; import dev.dinauer.metrics.service.model.Bucket; import dev.dinauer.metrics.service.model.BucketUnit; @@ -23,27 +27,39 @@ public class Service @Inject BucketConfigProvider bucketConfigProvider; - public void index(String resource, String metric, Map values) + @Inject + Logger LOG; + + public void index(String resource, String metric, String owner, Map values) { - index(resource, metric, values, ZonedDateTime.now(Clock.systemUTC())); + index(resource, metric, owner, values, ZonedDateTime.now(Clock.systemUTC())); } @Transactional - public void index(String resource, String metric, Map values, ZonedDateTime now) + public void index(String resource, String name, String owner, Map values, ZonedDateTime now) { for (BucketUnit unit : bucketConfigProvider.get()) { String timestamp = TimestampGenerator.generateTimestamp(now, unit); - Bucket metrics = repo.findByProperties(resource, metric, timestamp, unit).orElse(new Bucket(resource, metric, timestamp, now.toEpochSecond(), unit)); - for (Map.Entry entry : values.entrySet()) + Bucket bucket = repo.findByProperties(resource, name, timestamp, unit).orElse(new Bucket(resource, name, timestamp, now.toEpochSecond(), owner, unit)); + String bucketOwner = bucket.getOwner(); + if (Objects.isNull(bucketOwner) || Objects.equals(bucketOwner, owner)) { - Double value = entry.getValue(); - if (value != null) + for (Map.Entry entry : values.entrySet()) { - metrics.add(entry.getKey().toUpperCase(), entry.getValue()); + Double value = entry.getValue(); + if (value != null) + { + bucket.add(entry.getKey().toUpperCase(), entry.getValue()); + } } } - repo.persist(metrics); + else + { + LOG.info(String.format("Forbidden. Cannot add metrics to bucket %s/%s owned by %s in behalf of %s.", bucket.getResource(), bucket.getName(), bucketOwner, owner)); + throw new ForbiddenException(); + } + repo.persist(bucket); } } diff --git a/src/main/java/dev/dinauer/metrics/service/client/ClientRepo.java b/src/main/java/dev/dinauer/metrics/service/client/ClientRepo.java index 4d68a2f..1110cee 100644 --- a/src/main/java/dev/dinauer/metrics/service/client/ClientRepo.java +++ b/src/main/java/dev/dinauer/metrics/service/client/ClientRepo.java @@ -2,11 +2,14 @@ package dev.dinauer.metrics.service.client; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.logging.Logger; import io.quarkus.elytron.security.common.BcryptUtil; import io.quarkus.runtime.Startup; @@ -15,11 +18,12 @@ import io.quarkus.runtime.Startup; @ApplicationScoped public class ClientRepo { - private final static String CLIENT_PROPERTY_PREFIX = "dev.dinauer.metrics-service.client"; + private final static String CLIENT_PROPERTY_PREFIX = "big.bucket.client"; - List clients; + private final List clients; - public ClientRepo() + @Inject + public ClientRepo(Logger LOG) { List result = new ArrayList<>(); Config config = ConfigProvider.getConfig(); @@ -28,16 +32,16 @@ public class ClientRepo if (key.startsWith(CLIENT_PROPERTY_PREFIX)) { List sections = List.of(key.split("\\.")); - if (sections.size() == 6) + if (sections.size() == 5) { - String clientId = sections.get(4); - String permission = sections.get(5).toUpperCase(); - if (!clientId.isBlank() && List.of("RO", "RW").contains(permission)) + String clientId = sections.get(3); + String permission = sections.get(4).toUpperCase(); + if (!clientId.isBlank() && List.of("RO", "RW", "WO").contains(permission)) { String password = config.getValue(key, String.class); if (password != null && !password.isBlank()) { - Client client = new Client(clientId, BcryptUtil.bcryptHash(password), Permission.valueOf(permission)); + Client client = new Client(clientId.toLowerCase(), BcryptUtil.bcryptHash(password), Permission.valueOf(permission)); if (!result.contains(client)) { result.add(client); @@ -48,10 +52,15 @@ public class ClientRepo } } } + else + { + throw new IllegalArgumentException(String.format("Invalid permission %s for user %s", permission, clientId)); + } } } } this.clients = result; + LOG.infof("Running with clients: %s", clients.stream().map(client -> String.format("%s[%s]", client.id(), client.permission())).collect(Collectors.joining(","))); } public Client findById(String id) diff --git a/src/main/java/dev/dinauer/metrics/service/client/Permission.java b/src/main/java/dev/dinauer/metrics/service/client/Permission.java index 4f68a6e..75118b2 100644 --- a/src/main/java/dev/dinauer/metrics/service/client/Permission.java +++ b/src/main/java/dev/dinauer/metrics/service/client/Permission.java @@ -3,4 +3,4 @@ package dev.dinauer.metrics.service.client; public enum Permission { RW, RO, WO -} +} \ No newline at end of file diff --git a/src/main/java/dev/dinauer/metrics/service/client/auth/AuthenticationService.java b/src/main/java/dev/dinauer/metrics/service/client/auth/AuthenticationService.java index 59c6e61..641b22c 100644 --- a/src/main/java/dev/dinauer/metrics/service/client/auth/AuthenticationService.java +++ b/src/main/java/dev/dinauer/metrics/service/client/auth/AuthenticationService.java @@ -7,6 +7,8 @@ import jakarta.inject.Inject; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.HttpHeaders; +import org.jboss.resteasy.reactive.common.NotImplementedYet; + import io.quarkus.security.UnauthorizedException; import dev.dinauer.metrics.service.client.Client; @@ -50,7 +52,11 @@ public class AuthenticationService } case BEARER: { - return bearerAuthClientProvider.get(header.credentials()); + if (false) + { + return bearerAuthClientProvider.get(header.credentials()); + } + throw new NotImplementedYet(); } } throw new UnauthorizedException(); diff --git a/src/main/java/dev/dinauer/metrics/service/client/auth/BearerAuthClientProvider.java b/src/main/java/dev/dinauer/metrics/service/client/auth/BearerAuthClientProvider.java index 80c7ca0..6bf4eb3 100644 --- a/src/main/java/dev/dinauer/metrics/service/client/auth/BearerAuthClientProvider.java +++ b/src/main/java/dev/dinauer/metrics/service/client/auth/BearerAuthClientProvider.java @@ -4,8 +4,11 @@ import java.util.Optional; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; - import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.jwt.JsonWebToken; @@ -22,11 +25,18 @@ public class BearerAuthClientProvider @Inject JWTParser parser; - @ConfigProperty(name = "dev.dinauer.metrics-service.jwt.client.field") + @ConfigProperty(name = "big.bucket.jwt.client-id.field") String clientIdField; + @ConfigProperty(name = "big.bucket.auth.client-only") + Boolean clientOnly; + public Client get(String credentials) { + if (clientOnly) + { + throw new WebApplicationException(Response.status(403).type(MediaType.TEXT_PLAIN).entity("endusers_disabled").build()); + } try { JsonWebToken token = parser.parse(credentials); diff --git a/src/main/java/dev/dinauer/metrics/service/model/Bucket.java b/src/main/java/dev/dinauer/metrics/service/model/Bucket.java index 5ec611d..5d81950 100644 --- a/src/main/java/dev/dinauer/metrics/service/model/Bucket.java +++ b/src/main/java/dev/dinauer/metrics/service/model/Bucket.java @@ -25,7 +25,7 @@ public class Bucket @Column(nullable = false) private String resource; - @Column(name = "collection_name", nullable = false) + @Column(name = "bucket_name", nullable = false) private String name; @Column(nullable = false) @@ -55,14 +55,15 @@ public class Bucket { } - public Bucket(String resource, String name, String timestamp, long unixTimestamp, BucketUnit bucketUnit) + public Bucket(String resource, String name, String timestamp, long unixTimestamp, String owner, BucketUnit bucketUnit) { this.id = UUID.randomUUID().toString(); this.resource = resource; + this.name = name; this.timestamp = timestamp; this.unixTimestamp = unixTimestamp; + this.owner = owner; this.bucketUnit = bucketUnit; - this.name = name; this.metrics = "{}"; } @@ -164,4 +165,9 @@ public class Bucket this.updatedAt = updatedAt; return this; } + + public String getOwner() + { + return owner; + } } diff --git a/src/main/java/dev/dinauer/metrics/service/utils/BucketConfigProvider.java b/src/main/java/dev/dinauer/metrics/service/utils/BucketConfigProvider.java index 4479f4b..d6b05e6 100644 --- a/src/main/java/dev/dinauer/metrics/service/utils/BucketConfigProvider.java +++ b/src/main/java/dev/dinauer/metrics/service/utils/BucketConfigProvider.java @@ -18,7 +18,7 @@ import dev.dinauer.metrics.service.model.BucketUnit; @ApplicationScoped public class BucketConfigProvider { - private Set units; + private final Set units; @Inject public BucketConfigProvider(Logger LOG, @ConfigProperty(name = "dev.dinauer.metrics-service.buckets") String config) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8247b1e..08eb6ba 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,6 +9,9 @@ dev.dinauer.metrics-service.jwt.client.field=upn # JWT mp.jwt.verify.publickey.location=dev/publicKey.pem +%dev.big.bucket.jwt.client-id.field=upn +%dev.big.bucket.auth.client-only=false + # Postgres quarkus.datasource.db-kind = postgresql %dev.quarkus.datasource.username = postgres diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..66a53e1 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,6 @@ +________ _ ____ __ __ +___/ __ )(_)___ _/ __ )__ _______/ /_____ / /_ +__/ __ / / __ `/ __ / / / / ___/ //_/ _ \/ __/ +_/ /_/ / / /_/ / /_/ / /_/ / /__/ ,< / __/ /_ +/_____/_/\__, /_____/\__,_/\___/_/|_|\___/\__/ + /____/ \ No newline at end of file