Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,24 @@ services:
profiles:
- kibana

axonserver:
image: 'axoniq/axonserver:latest'
environment:
- 'AXONIQ_AXONSERVER_STANDALONE=TRUE'
ports:
- '8024:8024'
- '8124:8124'
profiles:
- axon

valkey:
image: valkey/valkey
ports:
- '6379:6379'
profiles:
- valkey
- redis

server:
image: openjdk:17
working_dir: /app
Expand Down
17 changes: 13 additions & 4 deletions server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ plugins {
id 'jacoco'
id 'nu.studer.jooq' version '8.2.1'
id 'de.undercouch.download' version '5.4.0'
id 'org.springframework.boot' version '3.3.10'
id 'org.springframework.boot' version '3.4.5'
id 'io.spring.dependency-management' version '1.1.0'
id 'io.gatling.gradle' version '3.9.5'
id 'java'
Expand All @@ -37,11 +37,13 @@ def versions = [
tika: '3.1.0',
bouncycastle: '1.80',
commons_lang3: '3.12.0',
httpclient5: '5.2.1',
jaxb_api: '2.3.1',
jaxb_impl: '2.3.8',
gatling: '3.13.5',
loki4j: '1.4.2'
loki4j: '1.4.2',
axon: '4.11.2',
jedis: '5.2.0',
embedded_redis: '1.4.3'
]
ext['junit-jupiter.version'] = versions.junit
sourceCompatibility = versions.java
Expand Down Expand Up @@ -84,6 +86,7 @@ dependencies {
implementation "org.springframework.boot:spring-boot-starter-actuator"
implementation "org.springframework.boot:spring-boot-starter-cache"
implementation "org.springframework.boot:spring-boot-starter-aop"
implementation "org.springframework.boot:spring-boot-starter-data-redis"
implementation "org.springframework.security:spring-security-oauth2-client"
implementation "org.springframework.security:spring-security-oauth2-jose"
implementation "org.springframework.session:spring-session-jdbc"
Expand All @@ -92,6 +95,9 @@ dependencies {
implementation "org.ehcache:ehcache:${versions.ehcache}"
implementation "com.giffing.bucket4j.spring.boot.starter:bucket4j-spring-boot-starter:${versions.bucket4j}"
implementation "org.jobrunr:jobrunr-spring-boot-3-starter:${versions.jobrunr}"
implementation("org.axonframework:axon-spring-boot-starter:${versions.axon}") {
exclude group: 'org.axonframework', module: 'axon-server-connector'
}
implementation "org.flywaydb:flyway-core:${versions.flyway}"
implementation "com.google.cloud:google-cloud-storage:${versions.gcloud}"
implementation "com.azure:azure-storage-blob:${versions.azure}"
Expand All @@ -106,12 +112,15 @@ dependencies {
implementation "javax.xml.bind:jaxb-api:${versions.jaxb_api}"
implementation "com.sun.xml.bind:jaxb-impl:${versions.jaxb_impl}"
implementation "org.apache.commons:commons-lang3:${versions.commons_lang3}"
implementation "org.apache.httpcomponents.client5:httpclient5:${versions.httpclient5}"
implementation "org.apache.httpcomponents.client5:httpclient5"
implementation "org.apache.tika:tika-core:${versions.tika}"
implementation "com.github.loki4j:loki-logback-appender:${versions.loki4j}"
implementation "io.micrometer:micrometer-tracing"
implementation "io.micrometer:micrometer-tracing-bridge-otel"
implementation "io.opentelemetry:opentelemetry-exporter-zipkin"
implementation "redis.clients:jedis:${versions.jedis}"
implementation "com.github.codemonstur:embedded-redis:${versions.embedded_redis}"

runtimeOnly "io.micrometer:micrometer-registry-prometheus"
runtimeOnly "org.postgresql:postgresql"
jooqGenerator "org.postgresql:postgresql"
Expand Down
2 changes: 1 addition & 1 deletion server/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
24 changes: 22 additions & 2 deletions server/src/dev/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ spring:
config: classpath:ehcache.xml
datasource:
url: jdbc:postgresql://localhost:5432/postgres
username: gitpod
password: gitpod
username: openvsx
password: openvsx
flyway:
schemas: public
baseline-on-migrate: true
baseline-version: 0.1.0
baseline-description: JobRunr tables
Expand Down Expand Up @@ -135,10 +136,29 @@ bucket4j:
time: 1
unit: seconds

axon:
serializer:
events: jackson

ovsx:
axon:
datasource:
jdbc-url: jdbc:postgresql://localhost:5432/postgres
username: openvsx
password: openvsx
schema: axon
jpa:
generate-ddl: true
properties:
hibernate:
dialect: org.eclipse.openvsx.axon.ByteaEnforcedPostgresSQLDialect
physical_naming_strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
redis:
embedded: true
databasesearch:
enabled: false
elasticsearch:
enabled: false
clear-on-start: true
eclipse:
base-url: https://api.eclipse.org
Expand Down
32 changes: 19 additions & 13 deletions server/src/main/java/org/eclipse/openvsx/UserAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
package org.eclipse.openvsx;

import jakarta.servlet.http.HttpServletRequest;
import org.axonframework.messaging.responsetypes.ResponseTypes;
import org.axonframework.queryhandling.QueryGateway;
import org.eclipse.openvsx.eclipse.EclipseService;
import org.eclipse.openvsx.entities.NamespaceMembership;
import org.eclipse.openvsx.entities.UserData;
import org.eclipse.openvsx.json.*;
import org.eclipse.openvsx.query.GetUserAccessTokens;
import org.eclipse.openvsx.repositories.RepositoryService;
import org.eclipse.openvsx.security.CodedAuthException;
import org.eclipse.openvsx.security.IdPrincipal;
import org.eclipse.openvsx.storage.StorageUtilService;
import org.eclipse.openvsx.util.ErrorResultException;
import org.eclipse.openvsx.util.NotFoundException;
Expand All @@ -27,6 +31,7 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.web.bind.annotation.*;
Expand All @@ -35,6 +40,7 @@

import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static org.eclipse.openvsx.entities.FileResource.*;
Expand All @@ -51,17 +57,20 @@ public class UserAPI {
private final UserService users;
private final EclipseService eclipse;
private final StorageUtilService storageUtil;
private final QueryGateway queries;

public UserAPI(
RepositoryService repositories,
UserService users,
EclipseService eclipse,
StorageUtilService storageUtil
StorageUtilService storageUtil,
QueryGateway queries
) {
this.repositories = repositories;
this.users = users;
this.eclipse = eclipse;
this.storageUtil = storageUtil;
this.queries = queries;
}

@GetMapping(
Expand Down Expand Up @@ -133,19 +142,16 @@ public CsrfTokenJson getCsrfToken(HttpServletRequest request) {
path = "/user/tokens",
produces = MediaType.APPLICATION_JSON_VALUE
)
public List<AccessTokenJson> getAccessTokens() {
var user = users.findLoggedInUser();
if (user == null) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
}
public CompletableFuture<List<AccessTokenJson>> getAccessTokens(@AuthenticationPrincipal IdPrincipal principal) {
var serverUrl = UrlUtil.getBaseUrl();
return repositories.findActiveAccessTokens(user)
.map(token -> {
var json = token.toAccessTokenJson();
json.setDeleteTokenUrl(createApiUrl(serverUrl, "user", "token", "delete", Long.toString(token.getId())));
return json;
})
.toList();
return queries.query(new GetUserAccessTokens(principal.getId()), ResponseTypes.multipleInstancesOf(AccessTokenJson.class))
.thenApply(tokens ->
tokens.stream().map(token -> {
token.setDeleteTokenUrl(serverUrl + token.getDeleteTokenUrl());
return token;
})
.toList()
);
}

@PostMapping(
Expand Down
15 changes: 13 additions & 2 deletions server/src/main/java/org/eclipse/openvsx/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@
import org.apache.tika.mime.MediaType;
import org.apache.tika.mime.MimeTypeException;
import org.apache.tika.mime.MimeTypes;
import org.axonframework.eventhandling.gateway.EventGateway;
import org.eclipse.openvsx.cache.CacheService;
import org.eclipse.openvsx.entities.Namespace;
import org.eclipse.openvsx.entities.NamespaceMembership;
import org.eclipse.openvsx.entities.PersonalAccessToken;
import org.eclipse.openvsx.entities.UserData;
import org.eclipse.openvsx.events.PersonalAccessTokenAccessed;
import org.eclipse.openvsx.events.PersonalAccessTokenCreated;
import org.eclipse.openvsx.events.PersonalAccessTokenDeleted;
import org.eclipse.openvsx.events.UserDataCreated;
import org.eclipse.openvsx.json.AccessTokenJson;
import org.eclipse.openvsx.json.NamespaceDetailsJson;
import org.eclipse.openvsx.json.ResultJson;
Expand Down Expand Up @@ -57,6 +62,7 @@ public class UserService {
private final ExtensionValidator validator;
private final ClientRegistrationRepository clientRegistrationRepository;
private final OAuth2AttributesConfig attributesConfig;
private final EventGateway events;

public UserService(
EntityManager entityManager,
Expand All @@ -65,7 +71,8 @@ public UserService(
CacheService cache,
ExtensionValidator validator,
@Autowired(required = false) ClientRegistrationRepository clientRegistrationRepository,
OAuth2AttributesConfig attributesConfig
OAuth2AttributesConfig attributesConfig,
EventGateway events
) {
this.entityManager = entityManager;
this.repositories = repositories;
Expand All @@ -74,6 +81,7 @@ public UserService(
this.validator = validator;
this.clientRegistrationRepository = clientRegistrationRepository;
this.attributesConfig = attributesConfig;
this.events = events;
}

public UserData findLoggedInUser() {
Expand All @@ -95,6 +103,7 @@ public PersonalAccessToken useAccessToken(String tokenValue) {
return null;
}
token.setAccessedTimestamp(TimeUtil.getCurrentUTC());
events.publish(new PersonalAccessTokenAccessed(token.getUser().getId(), token.getId(), TimeUtil.toUTCString(token.getAccessedTimestamp())));
return token;
}

Expand Down Expand Up @@ -261,7 +270,7 @@ public AccessTokenJson createAccessToken(UserData user, String description) {
// Include the token value after creation so the user can copy it
json.setValue(token.getValue());
json.setDeleteTokenUrl(createApiUrl(UrlUtil.getBaseUrl(), "user", "token", "delete", Long.toString(token.getId())));

events.publish(new PersonalAccessTokenCreated(user.getId(), token.getId(), TimeUtil.toUTCString(token.getCreatedTimestamp()), token.getDescription()));
return json;
}

Expand All @@ -278,6 +287,7 @@ public ResultJson deleteAccessToken(UserData user, long id) {
}

token.setActive(false);
events.publish(new PersonalAccessTokenDeleted(user.getId(), token.getId()));
return ResultJson.success("Deleted access token for user " + user.getLoginName() + ".");
}

Expand All @@ -291,6 +301,7 @@ public UserData upsertUser(UserData newUser) {
if (userData == null) {
entityManager.persist(newUser);
userData = newUser;
events.publish(new UserDataCreated(userData.getId()));
} else {
var updated = false;
if (!StringUtils.equals(userData.getLoginName(), newUser.getLoginName())) {
Expand Down
Loading
Loading