Skip to content

Commit d8c0328

Browse files
Merge pull request #600 from qbicsoftware/development
Release PR
2 parents bb996a0 + 5ad1d94 commit d8c0328

File tree

244 files changed

+8681
-3059
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

244 files changed

+8681
-3059
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
Data Manager - A web-based multi-omics data management platform for the biomedical life sciences
66
that enables FAIR-compliant data access.
77

8-
**Disclaimer**: _this project is still under heavy development and not yet officially released._
9-
108
[![Build Maven Package](https://github.com/qbicsoftware/data-manager-app/actions/workflows/build_package.yml/badge.svg)](https://github.com/qbicsoftware/data-manager-app/actions/workflows/build_package.yml)
119
[![Run Maven Tests](https://github.com/qbicsoftware/data-manager-app/actions/workflows/run_tests.yml/badge.svg)](https://github.com/qbicsoftware/data-manager-app/actions/workflows/run_tests.yml)
1210
[![CodeQL](https://github.com/qbicsoftware/data-manager-app/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/qbicsoftware/data-manager-app/actions/workflows/codeql-analysis.yml)
@@ -334,3 +332,5 @@ This work is licensed under the [MIT license](https://mit-license.org/).
334332

335333
This work uses the [Vaadin Framework](https://github.com/vaadin), which is licensed
336334
under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).
335+
336+
The University of Tübingen logo is a registered trademark and the copyright is owned by the [University of Tübingen](https://uni-tuebingen.de/).

application-commons/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,10 @@
1919
<groupId>org.springframework.data</groupId>
2020
<artifactId>spring-data-commons</artifactId>
2121
</dependency>
22+
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
23+
<dependency>
24+
<groupId>org.springframework.security</groupId>
25+
<artifactId>spring-security-core</artifactId>
26+
</dependency>
2227
</dependencies>
2328
</project>

application-commons/src/main/java/life/qbic/application/commons/ApplicationException.java

+4
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ public static ApplicationException wrapping(Throwable e) {
104104
if (e instanceof ApplicationException applicationException) {
105105
return new ApplicationException(e, applicationException.errorCode(),
106106
applicationException.errorParameters());
107+
}
108+
if (e instanceof org.springframework.security.access.AccessDeniedException) {
109+
return new ApplicationException(e, ErrorCode.ACCESS_DENIED, ErrorParameters.empty());
107110
} else {
108111
return new ApplicationException(e.getMessage(), e);
109112
}
@@ -133,6 +136,7 @@ public String toString() {
133136
*/
134137
public enum ErrorCode {
135138
GENERAL,
139+
ACCESS_DENIED,
136140
INVALID_EXPERIMENTAL_DESIGN,
137141
INVALID_PROJECT_OBJECTIVE,
138142
INVALID_PROJECT_TITLE,

domain-concept/pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
<artifactId>spock-core</artifactId>
2424
<scope>test</scope>
2525
</dependency>
26+
27+
<dependency>
28+
<groupId>com.fasterxml.jackson.core</groupId>
29+
<artifactId>jackson-annotations</artifactId>
30+
<version>2.17.1</version>
31+
</dependency>
2632
</dependencies>
2733

2834
</project>

domain-concept/src/main/java/life/qbic/domain/concepts/DomainEvent.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package life.qbic.domain.concepts;
22

3+
import com.fasterxml.jackson.annotation.JsonGetter;
34
import java.io.Serializable;
45
import java.time.Instant;
56

@@ -13,10 +14,21 @@
1314
*/
1415
public abstract class DomainEvent implements Serializable {
1516

17+
protected final Instant occurredOn;
18+
19+
protected DomainEvent() {
20+
this.occurredOn = Instant.now();
21+
}
22+
1623
/**
1724
* The instant of event creation.
1825
*
1926
* @return the instant the of event creation.
2027
*/
21-
public abstract Instant occurredOn();
28+
29+
@JsonGetter("occurredOn")
30+
public Instant occurredOn() {
31+
return occurredOn;
32+
}
33+
2234
}

domain-concept/src/main/java/life/qbic/domain/concepts/DomainEventSubscriber.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/**
44
* <b>Domain Event Subscriber</b>
55
*
6-
* <p>Clients can implement this interface to subscribe to {@link DomainEventPublisher} and get
6+
* <p>Clients can implement this interface to subscribe to {@link DomainEventDispatcher} and get
77
* informed whenever a {@link DomainEvent} of the specified type <code>T</code> happens.
88
*
99
* @since 1.0.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package life.qbic.domain.concepts;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
/**
7+
* <b>Local Domain Event Dispatcher</b>
8+
* <p>
9+
* Dispatches domain events to registered {@link DomainEventSubscriber}.
10+
* <p>
11+
* In contrast to the {@link DomainEventDispatcher} class, this class offers a
12+
* {@link LocalDomainEventDispatcher#reset()} method to clear all potentially existing subscribes.
13+
* This enables the domain event dispatcher to be used in an isolated local domain interaction
14+
* scope, e.g. from within an application service, when you want to ensure a successful committed
15+
* transaction first but still want to make use of broadcasting domain events within your domain.
16+
* <p>
17+
* <strong>Disclaimer</strong>
18+
* <p>The implementation runs in the main application thread and is blocking. Depending on the
19+
* number of registered subscriber and their implementation, expect the dispatching of events to
20+
* block your main app.</p>
21+
*
22+
* @since 1.0.0
23+
*/
24+
public class LocalDomainEventDispatcher {
25+
26+
private static final ThreadLocal<List<DomainEventSubscriber<?>>> subscribers = ThreadLocal.withInitial(
27+
ArrayList::new);
28+
private static LocalDomainEventDispatcher INSTANCE;
29+
30+
private LocalDomainEventDispatcher() {
31+
subscribers.set(new ArrayList<>());
32+
}
33+
34+
public static LocalDomainEventDispatcher instance() {
35+
if (INSTANCE == null) {
36+
INSTANCE = new LocalDomainEventDispatcher();
37+
}
38+
return INSTANCE;
39+
}
40+
41+
public <T extends DomainEvent> void subscribe(DomainEventSubscriber<T> subscriber) {
42+
var currentSubscribers = subscribers.get();
43+
currentSubscribers.add(subscriber);
44+
subscribers.set(currentSubscribers);
45+
}
46+
47+
public <T extends DomainEvent> void dispatch(T domainEvent) {
48+
subscribers.get().stream()
49+
.filter(subscriber -> subscriber.subscribedToEventType() == domainEvent.getClass())
50+
.map(subscriber -> (DomainEventSubscriber<T>) subscriber)
51+
.forEach(subscriber -> subscriber.handleEvent(domainEvent));
52+
}
53+
54+
/**
55+
* Removes all existing {@link DomainEventSubscriber}s of the dispatcher instance.
56+
*
57+
* @since 1.0.0
58+
*/
59+
public void reset() {
60+
subscribers.remove();
61+
}
62+
63+
}

domain-concept/src/test/groovy/life/qbic/domain/concepts/TestEvent.groovy

-5
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@ class TestEvent extends DomainEvent {
1616

1717
private String test = "TEST"
1818

19-
@Override
20-
Instant occurredOn() {
21-
return Instant.now()
22-
}
23-
2419
boolean equals(o) {
2520
if (this.is(o)) return true
2621
if (o == null || getClass() != o.class) return false

identity-api/src/main/java/life/qbic/identity/api/UserInfo.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
*
88
* @since 1.0.0
99
*/
10-
public record UserInfo(String id, String fullName, String emailAddress, String userName, String encryptedPassword,
10+
public record UserInfo(String id, String fullName, String emailAddress, String platformUserName,
11+
String encryptedPassword,
1112
boolean isActive) {
1213

1314
}

identity/src/main/java/life/qbic/identity/application/user/IdentityService.java

+22
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,28 @@ public ApplicationResponse requestPasswordReset(String userEmailAddress) {
150150
return ApplicationResponse.successResponse();
151151
}
152152

153+
public ApplicationResponse requestUserNameChange(String userId, String userName) {
154+
if (isNull(userName) || userName.isBlank()) {
155+
return ApplicationResponse.failureResponse(new EmptyUserNameException());
156+
}
157+
UserId id = UserId.from(userId);
158+
var optionalUser = userRepository.findById(id);
159+
if (optionalUser.isEmpty()) {
160+
return ApplicationResponse.failureResponse(new UserNotFoundException("User not found"));
161+
}
162+
// get user
163+
var user = optionalUser.get();
164+
if (user.userName().equals(userName)) {
165+
return ApplicationResponse.successResponse();
166+
}
167+
if (userRepository.findByUserName(userName).isPresent()) {
168+
return ApplicationResponse.failureResponse(new UserNameNotAvailableException());
169+
}
170+
user.setNewUserName(userName);
171+
userRepository.updateUser(user);
172+
return ApplicationResponse.successResponse();
173+
}
174+
153175
/**
154176
* Sets a new password for a given user.
155177
* <p>

identity/src/main/java/life/qbic/identity/domain/event/PasswordResetRequested.java

+2-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package life.qbic.identity.domain.event;
22

33
import java.io.Serial;
4-
import java.time.Instant;
54
import life.qbic.domain.concepts.DomainEvent;
65
import life.qbic.identity.domain.model.EmailAddress;
76
import life.qbic.identity.domain.model.FullName;
@@ -21,8 +20,6 @@ public class PasswordResetRequested extends DomainEvent {
2120

2221
private final UserId userId;
2322

24-
private final Instant occurredOn;
25-
2623
private final FullName fullName;
2724

2825
private final EmailAddress emailAddress;
@@ -38,23 +35,15 @@ public class PasswordResetRequested extends DomainEvent {
3835
*/
3936
public static PasswordResetRequested create(UserId userId, FullName name,
4037
EmailAddress emailAddress) {
41-
return new PasswordResetRequested(userId, Instant.now(), name, emailAddress);
38+
return new PasswordResetRequested(userId, name, emailAddress);
4239
}
4340

44-
private PasswordResetRequested(UserId userId, Instant occurredOn, FullName name,
45-
EmailAddress emailAddress) {
46-
super();
41+
private PasswordResetRequested(UserId userId, FullName name, EmailAddress emailAddress) {
4742
this.userId = userId;
48-
this.occurredOn = occurredOn;
4943
this.fullName = name;
5044
this.emailAddress = emailAddress;
5145
}
5246

53-
@Override
54-
public Instant occurredOn() {
55-
return occurredOn;
56-
}
57-
5847
/**
5948
* Returns the user id of the user for whom the password needs to be reset
6049
*

identity/src/main/java/life/qbic/identity/domain/event/UserActivated.java

-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.fasterxml.jackson.annotation.JsonGetter;
44
import java.io.Serial;
5-
import java.time.Instant;
65
import life.qbic.domain.concepts.DomainEvent;
76

87

@@ -25,17 +24,10 @@ private UserActivated() {
2524

2625
private UserActivated(String userId) {
2726
this.userId = userId;
28-
this.occurredOn = Instant.now();
2927
}
3028

31-
private Instant occurredOn;
3229
private String userId;
3330

34-
@Override
35-
public Instant occurredOn() {
36-
return occurredOn;
37-
}
38-
3931
@JsonGetter("userId")
4032
public String userId() {
4133
return userId;

identity/src/main/java/life/qbic/identity/domain/event/UserEmailConfirmed.java

+2-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package life.qbic.identity.domain.event;
22

33
import java.io.Serial;
4-
import java.time.Instant;
54
import life.qbic.domain.concepts.DomainEvent;
65

76
/**
@@ -16,27 +15,19 @@ public class UserEmailConfirmed extends DomainEvent {
1615

1716
private final String userId;
1817
private final String email;
19-
private final Instant occurredOn;
2018

2119
public static UserEmailConfirmed create(String userId, String email) {
22-
return new UserEmailConfirmed(userId, email, Instant.now());
20+
return new UserEmailConfirmed(userId, email);
2321
}
2422

2523
/**
2624
* @param userId the id of the user for which the mail was confirmed
2725
* @param email the confirmed email address
28-
* @param occurredOn the timestamp this event happened
2926
* @since 1.0.0
3027
*/
31-
private UserEmailConfirmed(final String userId, final String email, Instant occurredOn) {
28+
private UserEmailConfirmed(final String userId, final String email) {
3229
this.userId = userId;
3330
this.email = email;
34-
this.occurredOn = occurredOn;
35-
}
36-
37-
@Override
38-
public Instant occurredOn() {
39-
return occurredOn;
4031
}
4132

4233
public String userId() {

identity/src/main/java/life/qbic/identity/domain/event/UserRegistered.java

+1-11
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import com.fasterxml.jackson.annotation.JsonGetter;
55
import com.fasterxml.jackson.annotation.JsonProperty;
66
import java.io.Serial;
7-
import java.time.Instant;
87
import life.qbic.domain.concepts.DomainEvent;
98

109
/**
@@ -17,16 +16,14 @@ public class UserRegistered extends DomainEvent {
1716
@Serial
1817
private static final long serialVersionUID = 2581827831168895067L;
1918

20-
private Instant occurredOn;
21-
2219
@JsonProperty("fullName")
2320
private String fullName;
2421
@JsonProperty("email")
2522
private String email;
2623

2724
private String userId;
2825

29-
private UserRegistered() {
26+
protected UserRegistered() {
3027

3128
}
3229

@@ -43,13 +40,6 @@ private UserRegistered(final String userId, final String fullName, final String
4340
this.userId = userId;
4441
this.fullName = fullName;
4542
this.email = email;
46-
this.occurredOn = Instant.now();
47-
}
48-
49-
@JsonGetter("occurredOn")
50-
@Override
51-
public Instant occurredOn() {
52-
return occurredOn;
5343
}
5444

5545
@JsonGetter("userId")

identity/src/main/java/life/qbic/identity/domain/model/PasswordPolicy.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
class PasswordPolicy {
1717

18-
private static final int MIN_LENGTH = 8;
18+
private static final int MIN_LENGTH = 12;
1919

2020
private static PasswordPolicy policy;
2121

identity/src/main/java/life/qbic/identity/domain/model/User.java

+9
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,15 @@ public void setNewPassword(EncryptedPassword newPassword) {
171171
this.setEncryptedPassword(newPassword);
172172
}
173173

174+
/**
175+
* Overrides the previous username and sets a new one
176+
*
177+
* @param userName the new username
178+
*/
179+
public void setNewUserName(String userName) {
180+
this.userName = userName;
181+
}
182+
174183
private void activate() {
175184
if (this.active) {
176185
return;

identity/src/test/groovy/life/qbic/identity/domain/user/EncryptedPasswordSpec.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ class EncryptedPasswordSpec extends Specification {
2929
encryptedPassword != null
3030

3131
where:
32-
rawPassword << ["test1234", "1234!#234", "megastrongpassword"]
32+
rawPassword << ["test12340001", "1234!#2340001", "megastrongpassword0001"]
3333
}
3434
}

0 commit comments

Comments
 (0)