diff --git a/backend/explorer-core/autoconfigure/pom.xml b/backend/explorer-core/autoconfigure/pom.xml index 9a8b0b94e..e68720ea6 100644 --- a/backend/explorer-core/autoconfigure/pom.xml +++ b/backend/explorer-core/autoconfigure/pom.xml @@ -59,6 +59,28 @@ ${jdbc.driver.postgresql.version} + + + org.springframework.boot + spring-boot-starter-security + + + + com.github.theborakompanioni.bitcoin-spring-boot-starter + spring-lnurl-auth-starter + devel-SNAPSHOT + + + io.github.theborakompanioni + lnurl-simple + ${bitcoin-spring-boot-starter.version} + + io.minio @@ -153,6 +175,14 @@ + + + + + jitpack.io + https://jitpack.io + + diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/configuration/CachingConfiguration.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/configuration/CachingConfiguration.java index a9566d7a2..dd6342da3 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/configuration/CachingConfiguration.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/configuration/CachingConfiguration.java @@ -1,5 +1,6 @@ package org.royllo.explorer.core.configuration; +import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Configuration; @@ -8,6 +9,7 @@ */ @Configuration @EnableCaching +@RequiredArgsConstructor public class CachingConfiguration { } diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/user/User.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/user/User.java index ea20e6038..16d8f8873 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/user/User.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/user/User.java @@ -6,7 +6,6 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Table; -import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -18,6 +17,7 @@ import static jakarta.persistence.EnumType.STRING; import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PACKAGE; /** * Application user. @@ -26,7 +26,7 @@ @Setter @ToString @RequiredArgsConstructor -@AllArgsConstructor(access = AccessLevel.PACKAGE) +@AllArgsConstructor(access = PACKAGE) @Builder @Entity @Table(name = "APPLICATION_USER") diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/user/UserLnurlAuthKey.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/user/UserLnurlAuthKey.java new file mode 100644 index 000000000..15296ef63 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/user/UserLnurlAuthKey.java @@ -0,0 +1,54 @@ +package org.royllo.explorer.core.domain.user; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import static jakarta.persistence.FetchType.EAGER; +import static jakarta.persistence.GenerationType.IDENTITY; +import static lombok.AccessLevel.PACKAGE; + +/** + * Public key generated by the user's Lightning wallet. + * This key is unique to each user and service combination, ensuring that the user's identity is consistent with each service but not across different services. + */ +@Getter +@Setter +@ToString +@RequiredArgsConstructor +@AllArgsConstructor(access = PACKAGE) +@Builder +@Entity +@Table(name = "APPLICATION_USER_LNURL_AUTH_LINKING_KEY") +public class UserLnurlAuthKey { + + /** Unique identifier. */ + @Id + @Column(name = "ID") + @GeneratedValue(strategy = IDENTITY) + private Long id; + + /** User. */ + @ManyToOne(fetch = EAGER) + @JoinColumn(name = "FK_USER_OWNER", nullable = false) + private User owner; + + /** Linking key. */ + @Column(name = "LINKING_KEY", nullable = false) + private String linkingKey; + + /** K1: Randomly generated token that served as a challenge. */ + @Column(name = "K1", nullable = false) + private String k1; + +} diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/util/K1Value.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/util/K1Value.java new file mode 100644 index 000000000..a3347e831 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/util/K1Value.java @@ -0,0 +1,35 @@ +package org.royllo.explorer.core.domain.util; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.royllo.explorer.core.util.base.BaseDomain; + +import static lombok.AccessLevel.PACKAGE; + +/** + * K1 Value created by the system. + */ +@Getter +@Setter +@ToString +@RequiredArgsConstructor +@AllArgsConstructor(access = PACKAGE) +@Builder +@Entity +@Table(name = "UTIL_K1_CACHE") +public class K1Value extends BaseDomain { + + /** K1 (Unique identifier). */ + @Id + @Column(name = "K1") + private String k1; + +} diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/util/package-info.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/util/package-info.java new file mode 100644 index 000000000..fa4a8152e --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/domain/util/package-info.java @@ -0,0 +1,4 @@ +/** + * Utility domain objects. + */ +package org.royllo.explorer.core.domain.util; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/ContentService.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/ContentService.java index e507aa223..2b34af7d5 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/ContentService.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/ContentService.java @@ -13,4 +13,19 @@ public interface ContentService { */ void storeFile(byte[] fileContent, String fileName); + /** + * Check if a file exists. + * + * @param fileName file name + * @return true if file exists, false otherwise + */ + boolean fileExists(String fileName); + + /** + * Delete a file. + * + * @param fileName file name + */ + void deleteFile(String fileName); + } diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/LocalFileServiceImplementation.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/LocalFileServiceImplementation.java index 477601931..32b672b73 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/LocalFileServiceImplementation.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/LocalFileServiceImplementation.java @@ -6,12 +6,14 @@ import io.undertow.server.handlers.resource.PathResourceManager; import io.undertow.server.handlers.resource.ResourceHandler; import jakarta.annotation.PreDestroy; +import lombok.NonNull; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import javax.xml.bind.DatatypeConverter; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; @@ -100,7 +102,7 @@ public void onDestroy() throws Exception { @Override @SuppressWarnings("checkstyle:DesignForExtension") public void storeFile(final byte[] fileContent, - final String fileName) { + @NonNull final String fileName) { try { Files.write(fileSystem.getPath(".").resolve(fileName), fileContent); } catch (Exception e) { @@ -108,6 +110,22 @@ public void storeFile(final byte[] fileContent, } } + @Override + @SuppressWarnings("checkstyle:DesignForExtension") + public boolean fileExists(@NonNull final String fileName) { + return Files.exists(fileSystem.getPath(".").resolve(fileName)); + } + + @Override + @SuppressWarnings("checkstyle:DesignForExtension") + public void deleteFile(@NonNull final String fileName) { + try { + Files.delete(fileSystem.getPath(".").resolve(fileName)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** * Returns the sha256 value calculated with the parameter. * diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/S3ServiceImplementation.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/S3ServiceImplementation.java index 5bfb498cb..fd075afdf 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/S3ServiceImplementation.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/provider/storage/S3ServiceImplementation.java @@ -2,6 +2,9 @@ import io.minio.MinioClient; import io.minio.PutObjectArgs; +import io.minio.RemoveObjectArgs; +import io.minio.StatObjectArgs; +import io.minio.errors.ErrorResponseException; import lombok.NonNull; import org.apache.tika.Tika; import org.royllo.explorer.core.util.base.BaseService; @@ -41,17 +44,22 @@ public void updateS3Parameters(final S3Parameters newS3Parameters) { this.s3Parameters = newS3Parameters; } - @Override - public void storeFile(final byte[] fileContent, @NonNull final String fileName) { - // Creating a client. - MinioClient minioClient = MinioClient.builder() + /** + * Get Minio client. + * + * @return Minio client. + */ + private MinioClient getMinioClient() { + return MinioClient.builder() .endpoint(s3Parameters.getEndpointURL()) .credentials(s3Parameters.getAccessKey(), s3Parameters.getSecretKey()) .build(); + } - // Adding the file to the bucket. + @Override + public void storeFile(final byte[] fileContent, @NonNull final String fileName) { try { - minioClient.putObject(PutObjectArgs.builder() + getMinioClient().putObject(PutObjectArgs.builder() .bucket(s3Parameters.getBucketName()) .object(fileName).stream(new ByteArrayInputStream(fileContent), fileContent.length, -1) .contentType(new Tika().detect(fileContent)) @@ -61,4 +69,34 @@ public void storeFile(final byte[] fileContent, @NonNull final String fileName) } } + @Override + public boolean fileExists(@NonNull final String fileName) { + try { + getMinioClient().statObject(StatObjectArgs.builder() + .bucket(s3Parameters.getBucketName()) + .object(fileName) + .build()); + return true; + } catch (ErrorResponseException e) { + return false; + } catch (Exception e) { + logger.error("Error checking if file exists {} in S3: {}", fileName, e.getMessage()); + throw new RuntimeException(e.getMessage()); + } + } + + @Override + public void deleteFile(@NonNull final String fileName) { + try { + getMinioClient().removeObject( + RemoveObjectArgs.builder() + .bucket(s3Parameters.getBucketName()) + .object(fileName) + .build()); + } catch (Exception e) { + logger.error("Error deleting file {} in S3: {}", fileName, e.getMessage()); + throw new RuntimeException(e.getMessage()); + } + } + } diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/repository/user/UserLnurlAuthKeyRepository.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/repository/user/UserLnurlAuthKeyRepository.java new file mode 100644 index 000000000..ef19e0f0e --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/repository/user/UserLnurlAuthKeyRepository.java @@ -0,0 +1,31 @@ +package org.royllo.explorer.core.repository.user; + +import org.royllo.explorer.core.domain.user.UserLnurlAuthKey; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +/** + * {@link UserLnurlAuthKey} repository. + */ +@Repository +public interface UserLnurlAuthKeyRepository extends JpaRepository { + + /** + * Find a user lnurl-auth by the linking key. + * + * @param linkingKey linking key + * @return user lnurl-auth key + */ + Optional findByLinkingKey(String linkingKey); + + /** + * Find a user lnurl-auth by the k1. + * + * @param k1 k1 + * @return user lnurl-auth key + */ + Optional findByK1(String k1); + +} diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/repository/util/K1ValueRepository.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/repository/util/K1ValueRepository.java new file mode 100644 index 000000000..5c4d37715 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/repository/util/K1ValueRepository.java @@ -0,0 +1,24 @@ +package org.royllo.explorer.core.repository.util; + +import org.royllo.explorer.core.domain.util.K1Value; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.time.ZonedDateTime; +import java.util.List; + +/** + * {@link K1Value} repository. + */ +@Repository +public interface K1ValueRepository extends JpaRepository { + + /** + * Find all K1 values created before a given date. + * + * @param createdOn the date + * @return the list of K1 values + */ + List findByCreatedOnBefore(ZonedDateTime createdOn); + +} diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/repository/util/package-info.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/repository/util/package-info.java new file mode 100644 index 000000000..9552b0b2d --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/repository/util/package-info.java @@ -0,0 +1,4 @@ +/** + * util repository package. + */ +package org.royllo.explorer.core.repository.util; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/asset/AssetServiceImplementation.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/asset/AssetServiceImplementation.java index fe9932ecf..c01fe78d0 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/asset/AssetServiceImplementation.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/asset/AssetServiceImplementation.java @@ -1,5 +1,6 @@ package org.royllo.explorer.core.service.asset; +import com.fasterxml.jackson.databind.ObjectMapper; import io.micrometer.common.util.StringUtils; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -20,6 +21,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; +import java.io.IOException; import java.math.BigInteger; import java.time.ZonedDateTime; import java.util.Optional; @@ -102,21 +104,29 @@ public void updateAsset(final String assetId, if (metadata != null) { try { - // Decoding (same as using xxd -r -p) + // Decoding (same as using xxd -r -p). byte[] decodedBytes = Hex.decodeHex(metadata); // Detecting the file type. final String mimeType = new Tika().detect(decodedBytes); - final String extension = MimeTypes.getDefaultMimeTypes().forName(mimeType).getExtension(); + String extension = MimeTypes.getDefaultMimeTypes().forName(mimeType).getExtension(); + + // If we have a file extension ".txt", we check if it's a JSON. + if (".txt".equalsIgnoreCase(extension)) { + if (isJSONValid(new String(decodedBytes))) { + extension = ".json"; + } + } // Saving the file. final String fileName = assetId + extension; contentService.storeFile(decodedBytes, fileName); + logger.info("Asset id update for {}: Metadata saved as {}", assetId, fileName); // Setting the name of the file. assetToUpdate.get().setMetaDataFileName(fileName); } catch (DecoderException | MimeTypeException e) { - logger.error("Error decoding and saving metadata {}", e.getMessage()); + logger.error("Asset id update for {}: Error decoding and saving metadata {}", assetId, e.getMessage()); } } @@ -124,12 +134,14 @@ public void updateAsset(final String assetId, // If we have the new amount. if (amount != null) { assetToUpdate.get().setAmount(amount); + logger.info("Asset id update for {}: Amount updated to {}", assetId, amount); } // ============================================================================================================= // If we have the issuance date. if (issuanceDate != null) { assetToUpdate.get().setIssuanceDate(issuanceDate); + logger.info("Asset id update for {}: Issuance date updated to {}", assetId, issuanceDate); } // We save the asset with the new information. @@ -198,4 +210,20 @@ public Page getAssetsByAssetGroupId(final String assetGroupId, final i .map(ASSET_MAPPER::mapToAssetDTO); } + + /** + * Returns true if the string is a valid JSON. + * + * @param content string to check + * @return true if content is a valid JSON + */ + private boolean isJSONValid(final String content) { + try { + new ObjectMapper().readTree(content); + return true; + } catch (IOException e) { + return false; + } + } + } diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/user/UserService.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/user/UserService.java index 2227c2c87..479f0fc80 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/user/UserService.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/user/UserService.java @@ -23,6 +23,14 @@ public interface UserService { */ UserDTO getAnonymousUser(); + /** + * Create user. + * + * @param username user name + * @return user created + */ + UserDTO createUser(String username); + /** * Get user by its user id. * diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/user/UserServiceImplementation.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/user/UserServiceImplementation.java index 7ec0a9d74..3e5051682 100644 --- a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/user/UserServiceImplementation.java +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/user/UserServiceImplementation.java @@ -1,31 +1,49 @@ package org.royllo.explorer.core.service.user; +import io.micrometer.common.util.StringUtils; import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.royllo.explorer.core.domain.user.User; +import org.royllo.explorer.core.domain.user.UserLnurlAuthKey; import org.royllo.explorer.core.dto.user.UserDTO; +import org.royllo.explorer.core.repository.user.UserLnurlAuthKeyRepository; import org.royllo.explorer.core.repository.user.UserRepository; import org.royllo.explorer.core.util.base.BaseService; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; +import org.tbk.lnurl.auth.K1; +import org.tbk.lnurl.auth.LinkingKey; +import org.tbk.lnurl.auth.LnurlAuthPairingService; +import org.tbk.lnurl.simple.auth.SimpleLinkingKey; +import java.util.Collections; import java.util.Optional; +import java.util.UUID; import static org.royllo.explorer.core.util.constants.AdministratorUserConstants.ADMINISTRATOR_ID; import static org.royllo.explorer.core.util.constants.AnonymousUserConstants.ANONYMOUS_ID; +import static org.royllo.explorer.core.util.enums.UserRole.USER; /** * {@link UserService} implementation. + * Also implements {@link LnurlAuthPairingService} that brings the wallet and the web session together. */ -@SuppressWarnings("unused") @Service @RequiredArgsConstructor -public class UserServiceImplementation extends BaseService implements UserService { +@SuppressWarnings({"checkstyle:DesignForExtension", "unused"}) +public class UserServiceImplementation extends BaseService implements UserService, LnurlAuthPairingService, UserDetailsService { /** User repository. */ private final UserRepository userRepository; + /** User lnurl-auth key repository. */ + private final UserLnurlAuthKeyRepository userLnurlAuthKeyRepository; + @Override - public final UserDTO getAdministratorUser() { + public UserDTO getAdministratorUser() { logger.info("Getting administrator user"); final Optional administratorUser = userRepository.findById(ADMINISTRATOR_ID); @@ -39,7 +57,7 @@ public final UserDTO getAdministratorUser() { } @Override - public final UserDTO getAnonymousUser() { + public UserDTO getAnonymousUser() { logger.info("Getting anonymous user"); final Optional anonymousUser = userRepository.findById(ANONYMOUS_ID); @@ -53,7 +71,26 @@ public final UserDTO getAnonymousUser() { } @Override - public final Optional getUserByUserId(@NonNull final String userId) { + public UserDTO createUser(final String username) { + logger.info("Creating a user with username: {}", username); + + // Verification. + assert StringUtils.isNotEmpty(username) : "Username is required"; + assert userRepository.findByUsernameIgnoreCase(username.trim()).isEmpty() : "Username '" + username + "' already registered"; + + // Creation. + final User userCreated = userRepository.save(User.builder() + .userId(UUID.randomUUID().toString()) + .username(username.trim().toLowerCase()) + .role(USER) + .build()); + + logger.info("User created: {}", userCreated); + return USER_MAPPER.mapToUserDTO(userCreated); + } + + @Override + public Optional getUserByUserId(@NonNull final String userId) { logger.info("Getting user with userId: {}", userId); final Optional user = userRepository.findByUserId(userId); @@ -67,7 +104,7 @@ public final Optional getUserByUserId(@NonNull final String userId) { } @Override - public final Optional getUserByUsername(@NonNull final String username) { + public Optional getUserByUsername(@NonNull final String username) { logger.info("Getting user with username: {}", username); final Optional user = userRepository.findByUsernameIgnoreCase(username); @@ -80,4 +117,54 @@ public final Optional getUserByUsername(@NonNull final String username) } } + @Override + public boolean pairK1WithLinkingKey(@NonNull final K1 k1, @NonNull final LinkingKey linkingKey) { + // This method is called by the spring boot starter when a user has provided a k1 signed with a linking key and everything is valid. + // Usually, this is where you search if the linking key already exists in the database. + // If not, you create a new user and store the linking key. + // If yes, you just return the user, and you update the k1 used. + final Optional linkingKeyInDatabase = userLnurlAuthKeyRepository.findByLinkingKey(linkingKey.toHex()); + if (linkingKeyInDatabase.isEmpty()) { + logger.info("Creating the user for the linking key: {}", linkingKey.toHex()); + // We create the user. + final UserDTO user = createUser(linkingKey.toHex()); + // We create the user lnurl-auth key. + userLnurlAuthKeyRepository.save(UserLnurlAuthKey.builder() + .owner(USER_MAPPER.mapToUser(user)) + .linkingKey(linkingKey.toHex()) + .k1(k1.toHex()) + .build()); + } else { + logger.info("User with the linking key {} exists", linkingKey.toHex()); + linkingKeyInDatabase.get().setK1(k1.toHex()); + userLnurlAuthKeyRepository.save(linkingKeyInDatabase.get()); + } + return true; + } + + @Override + public Optional findPairedLinkingKeyByK1(@NonNull final K1 k1) { + // This method returns the linking key associated with the k1 passed as parameter. + logger.info("Finding the paired linking key for k1 {}", k1.toHex()); + final Optional linkingKey = userLnurlAuthKeyRepository.findByK1(k1.toHex()); + if (linkingKey.isPresent()) { + logger.info("Linking key found: {}", linkingKey.get().getLinkingKey()); + return Optional.of(SimpleLinkingKey.fromHex(linkingKey.get().getLinkingKey())); + } else { + logger.info("Linking key NOT found: {}", k1.toHex()); + return Optional.empty(); + } + } + + @Override + public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { + // Search for the user with it's linking key. + final UserLnurlAuthKey userLinkingKey = userLnurlAuthKeyRepository.findByLinkingKey(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username)); + + return new org.springframework.security.core.userdetails.User(userLinkingKey.getOwner().getUsername(), + UUID.randomUUID().toString(), + Collections.singletonList((new SimpleGrantedAuthority(userLinkingKey.getOwner().getRole().toString())))); + } + } diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/util/DatabaseK1Manager.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/util/DatabaseK1Manager.java new file mode 100644 index 000000000..f9599b12d --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/util/DatabaseK1Manager.java @@ -0,0 +1,50 @@ +package org.royllo.explorer.core.service.util; + +import lombok.RequiredArgsConstructor; +import org.royllo.explorer.core.domain.util.K1Value; +import org.royllo.explorer.core.repository.util.K1ValueRepository; +import org.tbk.lnurl.auth.K1; +import org.tbk.lnurl.auth.K1Factory; +import org.tbk.lnurl.auth.K1Manager; +import org.tbk.lnurl.auth.SimpleK1Factory; +import org.tbk.lnurl.simple.auth.SimpleK1; + +import java.time.ZonedDateTime; + +/** + * Database K1 manager. + */ +@RequiredArgsConstructor +@SuppressWarnings({"checkstyle:DesignForExtension"}) +public class DatabaseK1Manager implements K1Manager { + + /** Simple K1 factory (Generated with random). */ + private final K1Factory factory = new SimpleK1Factory(); + + /** K1 value repository. */ + private final K1ValueRepository repository; + + @Override + public boolean isValid(final K1 k1) { + // Purge old k1 before searching for it. + repository.findByCreatedOnBefore(ZonedDateTime.now().minusHours(1)) + .stream() + .map(k1Value -> SimpleK1.fromHex(k1Value.getK1())) + .forEach(this::invalidate); + + return repository.existsById(k1.toHex()); + } + + @Override + public void invalidate(final K1 k1) { + repository.delete(K1Value.builder().k1(k1.toHex()).build()); + } + + @Override + public K1 create() { + K1 k1 = factory.create(); + repository.save(K1Value.builder().k1(k1.toHex()).build()); + return k1; + } + +} diff --git a/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/util/package-info.java b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/util/package-info.java new file mode 100644 index 000000000..8f0714fc5 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/java/org/royllo/explorer/core/service/util/package-info.java @@ -0,0 +1,4 @@ +/** + * Utilities. + */ +package org.royllo.explorer.core.service.util; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/main/resources/META-INF/spring.factories b/backend/explorer-core/autoconfigure/src/main/resources/META-INF/spring.factories index 2e2d55626..eb2b80faa 100644 --- a/backend/explorer-core/autoconfigure/src/main/resources/META-INF/spring.factories +++ b/backend/explorer-core/autoconfigure/src/main/resources/META-INF/spring.factories @@ -1,4 +1,4 @@ -#org.springframework.boot.diagnostics.FailureAnalyzer=tech.cassandre.trading.bot.util.exception.ConfigurationFailureAnalyzer org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.royllo.explorer.core.configuration.DatabaseConfiguration,\ -org.royllo.explorer.core.configuration.ParametersConfiguration \ No newline at end of file +org.royllo.explorer.core.configuration.ParametersConfiguration,\ +org.royllo.explorer.core.configuration.CachingConfiguration \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/main/resources/application.properties b/backend/explorer-core/autoconfigure/src/main/resources/application.properties index 87c4b323d..b792135d1 100644 --- a/backend/explorer-core/autoconfigure/src/main/resources/application.properties +++ b/backend/explorer-core/autoconfigure/src/main/resources/application.properties @@ -11,7 +11,7 @@ royllo.explorer.content.base-url=https://content.royllo.org # ====================================================================================================================== # Database access configuration. spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver -spring.datasource.url=jdbc:hsqldb:mem:cassandre-database;DB_CLOSE_DELAY=-1 +spring.datasource.url=jdbc:hsqldb:mem:explorer-royllo-database spring.datasource.username=sa spring.datasource.password= # diff --git a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/specific/postgresql.xml b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/specific/postgresql.xml index 64c827338..5bdf59339 100644 --- a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/specific/postgresql.xml +++ b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/specific/postgresql.xml @@ -3,15 +3,12 @@ xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.4.xsd"> - 1:any create extension if not exists pg_trgm; - - CREATE INDEX INDEX_ASSET_GIN_NAME ON ASSET USING GIN (NAME gin_trgm_ops); - + CREATE INDEX INDEX_ASSET_GIN_NAME ON ASSET USING GIN (NAME gin_trgm_ops); \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table-constraints/table-constraints-application_user.xml b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table-constraints/table-constraints-application_user.xml index afc7c8f49..83783c024 100644 --- a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table-constraints/table-constraints-application_user.xml +++ b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table-constraints/table-constraints-application_user.xml @@ -2,7 +2,14 @@ - + + + + \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table-constraints/table-constraints-application_user_lnurl_auth_linking_key.xml b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table-constraints/table-constraints-application_user_lnurl_auth_linking_key.xml new file mode 100644 index 000000000..afc7c8f49 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table-constraints/table-constraints-application_user_lnurl_auth_linking_key.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-application_user_lnurl_auth_linking_key.xml b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-application_user_lnurl_auth_linking_key.xml new file mode 100644 index 000000000..80b47706e --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-application_user_lnurl_auth_linking_key.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-asset_state.xml b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-asset_state.xml index 89ff53ec8..92c32b868 100644 --- a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-asset_state.xml +++ b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-asset_state.xml @@ -3,7 +3,6 @@ xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.4.xsd"> - 1:any diff --git a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-util_k1_cache.xml b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-util_k1_cache.xml new file mode 100644 index 000000000..682ea8117 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/1.0.0/table/table-util_k1_cache.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/db.changelog-master.yaml b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/db.changelog-master.yaml index 2cbb16b38..51df75c7f 100644 --- a/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/backend/explorer-core/autoconfigure/src/main/resources/db/changelog/db.changelog-master.yaml @@ -5,7 +5,11 @@ databaseChangeLog: # Users. - include: - file: /db/changelog/1.0.0/table/table-application_user.xml + file: + /db/changelog/1.0.0/table/table-application_user.xml + - include: + file: + /db/changelog/1.0.0/table/table-application_user_lnurl_auth_linking_key.xml # Assets. - include: @@ -31,6 +35,11 @@ databaseChangeLog: - include: file: /db/changelog/1.0.0/table/table-proof.xml + # Users. + - include: + file: + /db/changelog/1.0.0/table/table-util_k1_cache.xml + # ==================================================================================================================== # 1.0.0 Table constraints definition @@ -45,6 +54,8 @@ databaseChangeLog: file: /db/changelog/1.0.0/table-constraints/table-constraints-bitcoin_transaction_output.xml - include: file: /db/changelog/1.0.0/table-constraints/table-constraints-application_user.xml + - include: + file: /db/changelog/1.0.0/table-constraints/table-constraints-application_user_lnurl_auth_linking_key.xml - include: file: /db/changelog/1.0.0/table-constraints/table-constraints-universe_server.xml - include: diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/AssetGroupServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/AssetGroupServiceTest.java similarity index 98% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/AssetGroupServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/AssetGroupServiceTest.java index 410d79503..871421caf 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/AssetGroupServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/AssetGroupServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.asset; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/AssetServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/AssetServiceTest.java similarity index 78% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/AssetServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/AssetServiceTest.java index e198695b3..c1ec1ac60 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/AssetServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/AssetServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.asset; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -360,6 +360,18 @@ public void updateAsset() { assertEquals(asset1.getAssetId() + ".png", assetUpdated.get().getMetaDataFileName()); assertEquals(0, new BigInteger("100").compareTo(assetUpdated.get().getAmount())); assertTrue(testDate.isEqual(assetUpdated.get().getIssuanceDate())); + + // ============================================================================================================= + // We test with a JSON File. + final String adamCoinMetadata = "227b226465736372697074696f6e223a20224120636f696e2064656469636174656420746f204164616d2066726f6d2047656e657369732c20746865206669727374206d616e206f6e2065617274682e20416c736f2061206669727374206173736574206d696e74656420696e20546972616d6973752077616c6c6574206f6e206d61696e6e65742e222c20226e616d65223a20224164616d436f696e222c20226163726f6e796d223a20224143222c202275736572223a20226661756365745f757365725f31222c2022656d61696c223a2022222c20226d696e7465645f7573696e67223a202268747470733a2f2f746573746e65742e7461726f77616c6c65742e6e65742f222c2022696d6167655f64617461223a2022646174613a696d6167652f6a70673b6261736536342c2f396a2f34414151536b5a4a5267414241514141415141424141442f32774244414d694b6c712b57666369766f362f6831636a752f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f327742444164586834662f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f7741415243414367414b41444153494141684542417845422f38514148774141415155424151454241514541414141414141414141414543417751464267634943516f4c2f3851417452414141674544417749454177554642415141414146394151494441415152425249684d5545474531466842794a7846444b426b61454949304b78775256533066416b4d324a7967676b4b4668635947526f6c4a69636f4b536f304e5459334f446b3651305246526b644953557054564656575631685a576d4e6b5a575a6e61476c7163335231646e643465587144684957476834694a69704b546c4a57576c35695a6d714b6a704b576d7036697071724b7a744c57327437693575734c44784d584778386a4a79744c54314e585731396a5a32754869342b546c3575666f3665727838765030396662332b506e362f38514148774541417745424151454241514542415141414141414141414543417751464267634943516f4c2f385141745245414167454342415144424163464241514141514a3341414543417845454253457842684a425551646863524d694d6f454946454b526f62484243534d7a55764156596e4c524368596b4e4f456c3852635947526f6d4a7967704b6a55324e7a67354f6b4e4552555a4853456c4b55315256566c64595756706a5a47566d5a326870616e4e3064585a3365486c36676f4f456859614869496d4b6b704f556c5a61586d4a6d616f714f6b7061616e714b6d7173724f3074626133754c6d367773504578636248794d6e4b3074505531646258324e6e613475506b3565626e364f6e7138765030396662332b506e362f396f4144414d4241414952417845415077434d6a42704b6d70724c6e703170697552305575302b6c49526a725347464646464142525252514155555555414646464641425252525141555555554146464646414252525251424e52535a48725453343755795239495642706d382b6c42633044734e49776355555539597965547750317044475534527365325072556f3272393066352b74495750722b564b3444664b50714b504a392f3070324365782f45306667507a6f4161596a36696d6c4748616e382b2f35307566582f43693479476970574150616d4663644b5968744646464142525252514155555555414646464641425252556b532f784838503861414652416f334e312f6c2f77445870784f66384b43632f7742422f582f5051556f475063306745436b396550616e594136554667426b314730685033525141386e74546161417a594f65445432544a42427869697777413570574978696d5959444f546e7350386154635239345557415870307065473664615145487052534161792b6c4d71587231707369344f5254454d6f6f6f7067464646464142525252514171727559437032394f77362f30464d68484262384b643149392b542f414a2f4b67425648633936474f31636d6e564337626a6a4f425373416e332b57622f363153717058766b5647713835794d65347151664b4f76483871594471544e423745476a48507451415647344a4a39425478366b2b76383652766d484641454979447855696e497068427a78316f35567552696b41382b7448576c7050616b4d6a497763556c4f666e42707455494b4b4b4b4143696969674364526949652f7744576c48336a2b5648384b2f682f4b68652f314e4a674448436d6f534d6438314d78414850725565437a4768414b754d444834342f6e2b46504f4d41446b31477041552b7450586b6261594337654b43636744756154357830352b7638416a536b5935787a514162514254547a2f414a37555964757449526a6967426a636367307056334737673030395451474936476743526334356f50576b5535424a3961576b4d61333354544b6c49345030714b6843436969696d41555555554154352b5666772f6c5372332b744d5535516533394b6550764833352f7a2b6c5377456b2b35544e35505370534d67696f56516e706a696d67486244672b76387853786e6e3844516f4b484a4f6330787547794b5945394a6b35786a38615253536f497853382b3141436e70554a4f4455684f4279616a54356d79653141434d75464761622b465062356a545370484e41446b2b37532f77434e47414647442b4648656b4d582b467638397159553944542f414f48366d696d684d6a3247676f52556c464d5679476969696b4d66476531536476702f4c2f3841562f4b6f4163484e5441385a2f77412f35464a6753557a6f782f4f6c48484835663455704761414774794b615275464b66656b397159434258586c66307054493354627a533434366b5530357a316f41546c75744f4879703961546236304830394b41416355756565656c4a547541754f357044457a514f6e316f417a394b584f4f6151444a4479414f314d6f4a796330565168636e3170435365706f6f6f414b4b4b4b41436e6f324f4430706c4641453252305054742f6e2b564f42787733352f3537314372646a55675048504970414b787a785463656c4c6a2b36632f352f774139615436385544444a704f63394b576c41396141454753616474413638306f474254533265425341516e4a6f41794f507850394253376637332b663841507451547836436741506f4f6c5275326542306f5a73384470546159676f6f6f706746464646414252525251415555555541464b435230707972334e506f734b34774d506f6165435436476b363030726a6c614c447550494864615441715065337253377a53416b7750536c336363635644755072535a7a525943517550716159574a704b4b5942525252514155555555414646464641425467704e4b4550656e3078584762423730465051302b696756776f6f6f6f414b4b4b4b414954316f71586150536a6150536764794b696e4d6d4f52546151776f6f6f6f414b4b4b4b414369696967416f705655734f4b556f5251422f2f396b3d227d22"; + assetService.updateAsset(asset1.getAssetId(), adamCoinMetadata, null, null); + + // We test the data. + assetUpdated = assetService.getAssetByAssetId(asset1.getAssetId()); + assertTrue(assetUpdated.isPresent()); + assertEquals(asset1.getAssetId() + ".json", assetUpdated.get().getMetaDataFileName()); + assertEquals(0, new BigInteger("100").compareTo(assetUpdated.get().getAmount())); + assertTrue(testDate.isEqual(assetUpdated.get().getIssuanceDate())); } @Test diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/AssetStateServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/AssetStateServiceTest.java similarity index 99% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/AssetStateServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/AssetStateServiceTest.java index f7c87bdcb..f0777ba8f 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/AssetStateServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/AssetStateServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.asset; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/package-info.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/package-info.java new file mode 100644 index 000000000..c7c8e0b3c --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/asset/package-info.java @@ -0,0 +1,4 @@ +/** + * Asset services tests. + */ +package org.royllo.explorer.core.test.core.service.asset; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/BitcoinServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/bitcoin/BitcoinServiceTest.java similarity index 98% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/BitcoinServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/bitcoin/BitcoinServiceTest.java index 4a3b09029..9053391c2 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/BitcoinServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/bitcoin/BitcoinServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.bitcoin; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/ProofServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/proof/ProofServiceTest.java similarity index 99% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/ProofServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/proof/ProofServiceTest.java index 575e378eb..f34dd74bc 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/ProofServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/proof/ProofServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.proof; import okhttp3.OkHttpClient; import okhttp3.Request; diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/proof/package-info.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/proof/package-info.java new file mode 100644 index 000000000..df41d0e45 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/proof/package-info.java @@ -0,0 +1,4 @@ +/** + * Proof service tests. + */ +package org.royllo.explorer.core.test.core.service.proof; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/RequestServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/request/RequestServiceTest.java similarity index 99% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/RequestServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/request/RequestServiceTest.java index 3666b5687..b5d1acaa9 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/RequestServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/request/RequestServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.request; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer; diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/request/package-info.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/request/package-info.java new file mode 100644 index 000000000..0aba417ba --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/request/package-info.java @@ -0,0 +1,4 @@ +/** + * Request service tests. + */ +package org.royllo.explorer.core.test.core.service.request; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/SearchServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/search/SearchServiceTest.java similarity index 98% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/SearchServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/search/SearchServiceTest.java index 2e2e784b0..267d7bb7d 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/SearchServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/search/SearchServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.search; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/search/package-info.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/search/package-info.java new file mode 100644 index 000000000..4dd6e5abe --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/search/package-info.java @@ -0,0 +1,4 @@ +/** + * Search services tests. + */ +package org.royllo.explorer.core.test.core.service.search; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/StatisticServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/statistics/StatisticServiceTest.java similarity index 97% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/StatisticServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/statistics/StatisticServiceTest.java index 33ea61818..c9eb6ae5a 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/StatisticServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/statistics/StatisticServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.statistics; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/statistics/package-info.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/statistics/package-info.java new file mode 100644 index 000000000..28babe6b5 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/statistics/package-info.java @@ -0,0 +1,4 @@ +/** + * Statistics service tests. + */ +package org.royllo.explorer.core.test.core.service.statistics; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/LocalFileServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/storage/LocalFileServiceTest.java similarity index 74% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/LocalFileServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/storage/LocalFileServiceTest.java index e025ebb9d..239b85453 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/LocalFileServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/storage/LocalFileServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.storage; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -14,6 +14,7 @@ import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.royllo.explorer.core.provider.storage.LocalFileServiceImplementation.WEB_SERVER_HOST; @@ -82,4 +83,37 @@ public void storeAndGetFile() { } } + + @Test + @DisplayName("File exists") + public void fileExists() { + // The file should not exist. + assertFalse(localFileServiceImplementation.fileExists("fileExistsTest.txt")); + + // We create the file. + localFileServiceImplementation.storeFile("Hello World!".getBytes(), "fileExistsTest.txt"); + + // The file should now exist. + assertTrue(localFileServiceImplementation.fileExists("fileExistsTest.txt")); + } + + @Test + @DisplayName("Delete file") + public void deleteFile() { + // The file should not exist. + assertFalse(localFileServiceImplementation.fileExists("fileDeleteTest.txt")); + + // We create the file. + localFileServiceImplementation.storeFile("Hello World!".getBytes(), "fileDeleteTest.txt"); + + // The file should now exist. + assertTrue(localFileServiceImplementation.fileExists("fileDeleteTest.txt")); + + // We delete the file. + localFileServiceImplementation.deleteFile("fileDeleteTest.txt"); + + // The file should not exist anymore. + assertFalse(localFileServiceImplementation.fileExists("fileDeleteTest.txt")); + } + } diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/storage/package-info.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/storage/package-info.java new file mode 100644 index 000000000..2ead87c32 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/storage/package-info.java @@ -0,0 +1,4 @@ +/** + * Storage services test. + */ +package org.royllo.explorer.core.test.core.service.storage; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/UniverseServerServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/universe/UniverseServerServiceTest.java similarity index 98% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/UniverseServerServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/universe/UniverseServerServiceTest.java index 203d4421c..be16bff17 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/UniverseServerServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/universe/UniverseServerServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.universe; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/universe/package-info.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/universe/package-info.java new file mode 100644 index 000000000..384756698 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/universe/package-info.java @@ -0,0 +1,4 @@ +/** + * Universe service tests. + */ +package org.royllo.explorer.core.test.core.service.universe; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/LnurlAuthPairingServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/LnurlAuthPairingServiceTest.java new file mode 100644 index 000000000..2babe031d --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/LnurlAuthPairingServiceTest.java @@ -0,0 +1,108 @@ +package org.royllo.explorer.core.test.core.service.user; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.royllo.explorer.core.domain.user.UserLnurlAuthKey; +import org.royllo.explorer.core.dto.user.UserDTO; +import org.royllo.explorer.core.repository.user.UserLnurlAuthKeyRepository; +import org.royllo.explorer.core.repository.user.UserRepository; +import org.royllo.explorer.core.service.user.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.tbk.lnurl.auth.K1; +import org.tbk.lnurl.auth.LinkingKey; +import org.tbk.lnurl.auth.LnurlAuthPairingService; +import org.tbk.lnurl.simple.auth.SimpleK1; +import org.tbk.lnurl.simple.auth.SimpleLinkingKey; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.royllo.explorer.core.util.enums.UserRole.USER; + +@SpringBootTest +@DirtiesContext +@DisplayName("LnurlAuthPairingService tests") +@SuppressWarnings({"checkstyle:DesignForExtension", "unused"}) +public class LnurlAuthPairingServiceTest { + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserLnurlAuthKeyRepository userLnurlAuthKeyRepository; + + @Autowired + private UserService userService; + + @Autowired + private LnurlAuthPairingService lnurlAuthPairingService; + + @Test + @DisplayName("pairK1WithLinkingKey() and findPairedLinkingKeyByK1()") + void lnurlAuthPairingService() { + // Unknown user login information. + String simpleK1Value = "e2af6254a8df433264fa23f67eb8188635d15ce883e8fc020989d5f82ae6f11e"; + String anotherK1Value = "d4f067cf72b94baddac1b3856b231669c3239d59cce95ef260da58363fb01822"; + String linkingKey1Value = "02c3b844b8104f0c1b15c507774c9ba7fc609f58f343b9b149122e944dd20c9362"; + K1 k1Test1 = SimpleK1.fromHex(simpleK1Value); + K1 k1Test2 = SimpleK1.fromHex(anotherK1Value); + LinkingKey linkingKeyTest1 = SimpleLinkingKey.fromHex(linkingKey1Value); + + // We clean the data. + userLnurlAuthKeyRepository.findByLinkingKey(linkingKey1Value).ifPresent(userLnurlAuthKey -> userLnurlAuthKeyRepository.delete(userLnurlAuthKey)); + userRepository.findByUsernameIgnoreCase(linkingKey1Value).ifPresent(user -> userRepository.delete(user)); + + // We check the data we have. + final long userCount = userRepository.count(); + final long userLnurlAuthKeyCount = userLnurlAuthKeyRepository.count(); + + + // ============================================================================================================= + // Test 1 : a new user logs in (it doesn't exist in database). + assertFalse(userService.getUserByUsername(linkingKey1Value).isPresent()); + assertFalse(userLnurlAuthKeyRepository.findByK1(simpleK1Value).isPresent()); + + // We pair & check if the data is here. + lnurlAuthPairingService.pairK1WithLinkingKey(k1Test1, linkingKeyTest1); + + // We check the number of data created. + assertEquals(userCount + 1, userRepository.count()); + assertEquals(userLnurlAuthKeyCount + 1, userLnurlAuthKeyRepository.count()); + + // We check the user created. + Optional newUserCreated = userService.getUserByUsername(linkingKey1Value); + assertTrue(newUserCreated.isPresent()); + assertNotNull(newUserCreated.get().getId()); + assertEquals(linkingKey1Value, newUserCreated.get().getUsername()); + assertEquals(USER, newUserCreated.get().getRole()); + + // We check the user lnurl-auth key created. + Optional newUserLinkingKeyCreated = userLnurlAuthKeyRepository.findByLinkingKey(linkingKey1Value); + assertTrue(newUserLinkingKeyCreated.isPresent()); + assertNotNull(newUserLinkingKeyCreated.get().getId()); + assertEquals(newUserCreated.get().getId(), newUserLinkingKeyCreated.get().getOwner().getId()); + assertEquals(simpleK1Value, newUserLinkingKeyCreated.get().getK1()); + assertEquals(linkingKey1Value, newUserLinkingKeyCreated.get().getLinkingKey()); + assertTrue(userLnurlAuthKeyRepository.findByK1(simpleK1Value).isPresent()); + + // ============================================================================================================= + // Test 2 : the same user logs again with another k1 - No new data but k1 updated. + lnurlAuthPairingService.pairK1WithLinkingKey(k1Test2, linkingKeyTest1); + assertEquals(userCount + 1, userRepository.count()); + assertEquals(userLnurlAuthKeyCount + 1, userLnurlAuthKeyRepository.count()); + + // We check the user lnurl-auth key updated. + newUserLinkingKeyCreated = userLnurlAuthKeyRepository.findByLinkingKey(linkingKey1Value); + assertTrue(newUserLinkingKeyCreated.isPresent()); + assertNotNull(newUserLinkingKeyCreated.get().getId()); + assertEquals(newUserCreated.get().getId(), newUserLinkingKeyCreated.get().getOwner().getId()); + assertEquals(anotherK1Value, newUserLinkingKeyCreated.get().getK1()); + assertEquals(linkingKey1Value, newUserLinkingKeyCreated.get().getLinkingKey()); + } + +} diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/UserDetailsServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/UserDetailsServiceTest.java new file mode 100644 index 000000000..b619e2964 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/UserDetailsServiceTest.java @@ -0,0 +1,54 @@ +package org.royllo.explorer.core.test.core.service.user; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.test.annotation.DirtiesContext; +import org.tbk.lnurl.auth.K1; +import org.tbk.lnurl.auth.LinkingKey; +import org.tbk.lnurl.auth.LnurlAuthPairingService; +import org.tbk.lnurl.simple.auth.SimpleK1; +import org.tbk.lnurl.simple.auth.SimpleLinkingKey; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@SpringBootTest +@DirtiesContext +@DisplayName("UserDetailsService tests") +public class UserDetailsServiceTest { + + @Autowired + private LnurlAuthPairingService lnurlAuthPairingService; + + @Autowired + private UserDetailsService userDetailsService; + + @Test + @DisplayName("loadUserByUsername()") + void loadUserByUsername() { + // New user. + String user1K1Value = "e2af6254a8df433264fa23f67eb8188635d15ce883e8fc020989d5f82ae6f11e"; + String user1linkingKey1Value = "02c3b844b8104f0c1b15c507774c9ba7fc609f58f343b9b149122e944dd20c9362"; + K1 user1K1Test1 = SimpleK1.fromHex(user1K1Value); + LinkingKey user1LinkingKeyTest1 = SimpleLinkingKey.fromHex(user1linkingKey1Value); + + // We create a new user. + lnurlAuthPairingService.pairK1WithLinkingKey(user1K1Test1, user1LinkingKeyTest1); + + // We try the load the user created. + final UserDetails userDetails = userDetailsService.loadUserByUsername(user1linkingKey1Value); + assertNotNull(userDetails); + assertEquals(user1linkingKey1Value, userDetails.getUsername()); + + // We try to load a non-existing user. + UsernameNotFoundException e = assertThrows(UsernameNotFoundException.class, () -> userDetailsService.loadUserByUsername("NON_EXISTING_USERNAME")); + assertEquals("User not found with username: NON_EXISTING_USERNAME", e.getMessage()); + } + +} diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/UserServiceTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/UserServiceTest.java similarity index 71% rename from backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/UserServiceTest.java rename to backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/UserServiceTest.java index ec7655fa0..048c67dd1 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/UserServiceTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/UserServiceTest.java @@ -1,4 +1,4 @@ -package org.royllo.explorer.core.test.core.service; +package org.royllo.explorer.core.test.core.service.user; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.royllo.explorer.core.util.constants.AdministratorUserConstants.ADMINISTRATOR_ID; import static org.royllo.explorer.core.util.constants.AdministratorUserConstants.ADMINISTRATOR_USER; @@ -23,6 +24,7 @@ import static org.royllo.explorer.core.util.constants.AnonymousUserConstants.ANONYMOUS_USER_ID; import static org.royllo.explorer.core.util.constants.AnonymousUserConstants.ANONYMOUS_USER_USERNAME; import static org.royllo.explorer.core.util.enums.UserRole.ADMINISTRATOR; +import static org.royllo.explorer.core.util.enums.UserRole.USER; @SpringBootTest @DirtiesContext @@ -54,6 +56,31 @@ public void getAnonymousUserTest() { assertEquals(ANONYMOUS_USER.getRole(), anonymousUser.getRole()); } + @Test + @DisplayName("createUser()") + public void createUserTest() { + // Creating a user with empty name or null. + AssertionError e = assertThrows(AssertionError.class, () -> userService.createUser(null)); + assertEquals("Username is required", e.getMessage()); + e = assertThrows(AssertionError.class, () -> userService.createUser("")); + assertEquals("Username is required", e.getMessage()); + + // Creating an existing user. + e = assertThrows(AssertionError.class, () -> userService.createUser(ANONYMOUS_USER_USERNAME)); + assertEquals("Username 'anonymous' already registered", e.getMessage()); + e = assertThrows(AssertionError.class, () -> userService.createUser(ADMINISTRATOR_USER_USERNAME)); + assertEquals("Username 'administrator' already registered", e.getMessage()); + e = assertThrows(AssertionError.class, () -> userService.createUser("straumat")); + assertEquals("Username 'straumat' already registered", e.getMessage()); + + // Creating a new user. + final UserDTO newUser = userService.createUser("newUser"); + assertNotNull(newUser); + assertNotNull(newUser.getId()); + assertEquals("newuser", newUser.getUsername()); + assertEquals(USER, newUser.getRole()); + } + @Test @DisplayName("getUserByUsername()") public void getUserByUsernameTest() { diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/package-info.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/package-info.java new file mode 100644 index 000000000..2ef1bbb64 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/user/package-info.java @@ -0,0 +1,4 @@ +/** + * User service tests. + */ +package org.royllo.explorer.core.test.core.service.user; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/util/DatabaseK1ManagerTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/util/DatabaseK1ManagerTest.java new file mode 100644 index 000000000..111c7f0dd --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/util/DatabaseK1ManagerTest.java @@ -0,0 +1,91 @@ +package org.royllo.explorer.core.test.core.service.util; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.royllo.explorer.core.domain.util.K1Value; +import org.royllo.explorer.core.repository.util.K1ValueRepository; +import org.royllo.explorer.core.service.util.DatabaseK1Manager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.tbk.lnurl.auth.K1; +import org.tbk.lnurl.simple.auth.SimpleK1; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest +@DirtiesContext +@DisplayName("DatabaseK1Manager tests") +public class DatabaseK1ManagerTest { + + @Autowired + private DataSource dataSource; + + @Autowired + private K1ValueRepository k1ValueRepository; + + @Test + @DisplayName("Create, retrieve and delete a K1 value") + public void databaseK1Manager() { + DatabaseK1Manager databaseK1Manager = new DatabaseK1Manager(k1ValueRepository); + + k1ValueRepository.deleteAll(); + + // K1 value for test. + String nonExistingK1Value = "e2af6254a8df433264fa23f67eb8188635d15ce883e8fc020989d5f82ae6f11e"; + K1 nonExistingK1 = SimpleK1.fromHex(nonExistingK1Value); + + // Checking k1 value creation. + assertEquals(0, k1ValueRepository.count()); + final K1 k1Created = databaseK1Manager.create(); + assertEquals(1, k1ValueRepository.count()); + + // Checking isValid() method. + assertTrue(databaseK1Manager.isValid(k1Created)); + assertFalse(databaseK1Manager.isValid(nonExistingK1)); + + // Checking invalidate() method. + databaseK1Manager.invalidate(k1Created); + databaseK1Manager.invalidate(nonExistingK1); + assertEquals(0, k1ValueRepository.count()); + assertFalse(databaseK1Manager.isValid(k1Created)); + assertFalse(databaseK1Manager.isValid(nonExistingK1)); + } + + @Test + @DisplayName("Purge old K1 values") + public void oldK1Purge() throws SQLException { + DatabaseK1Manager databaseK1Manager = new DatabaseK1Manager(k1ValueRepository); + k1ValueRepository.deleteAll(); + + // Test values. + String firstK1Value = "e2af6254a8df433264fa23f67eb8188635d15ce883e8fc020989d5f82ae6f11e"; + String secondK1Value = "d4f067cf72b94baddac1b3856b231669c3239d59cce95ef260da58363fb01822"; + + // Creating two values. + k1ValueRepository.save(K1Value.builder().k1(firstK1Value).build()); + k1ValueRepository.save(K1Value.builder().k1(secondK1Value).build()); + assertTrue(databaseK1Manager.isValid(SimpleK1.fromHex(firstK1Value))); + assertTrue(databaseK1Manager.isValid(SimpleK1.fromHex(secondK1Value))); + + // Updating the first value to change its creation date. + try (Connection connection = dataSource.getConnection()) { + connection.createStatement().executeQuery(""" + UPDATE UTIL_K1_CACHE + SET CREATED_ON = '2021-01-01 00:00:00' + WHERE K1 = '""" + firstK1Value + "'"); + } + + // Checking isValid() method (will trigger purge so k1 won't exist anymore. + assertFalse(databaseK1Manager.isValid(SimpleK1.fromHex(firstK1Value))); + assertTrue(databaseK1Manager.isValid(SimpleK1.fromHex(secondK1Value))); + + } + +} diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/util/package-info.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/util/package-info.java new file mode 100644 index 000000000..528a422b0 --- /dev/null +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/core/service/util/package-info.java @@ -0,0 +1,4 @@ +/** + * Util service tests. + */ +package org.royllo.explorer.core.test.core.service.util; \ No newline at end of file diff --git a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/integration/storage/S3ServiceImplementationTest.java b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/integration/storage/S3ServiceImplementationTest.java index 2607ebb3c..aaee602cd 100644 --- a/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/integration/storage/S3ServiceImplementationTest.java +++ b/backend/explorer-core/autoconfigure/src/test/java/org/royllo/explorer/core/test/integration/storage/S3ServiceImplementationTest.java @@ -34,6 +34,7 @@ import java.security.NoSuchAlgorithmException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -99,6 +100,7 @@ public void s3ImplementationTest() throws DecoderException { } // Adding the file. + assertFalse(contentService.fileExists("test.txt")); contentService.storeFile("test".getBytes(), "test.txt"); // Adding the file (again) - Checking there is no exception. @@ -107,6 +109,7 @@ public void s3ImplementationTest() throws DecoderException { // Checking that the file now exists. try { // File exists ? + assertTrue(contentService.fileExists("test.txt")); minioClient.statObject(StatObjectArgs.builder().bucket(S3_BUCKET_NAME).object("test.txt").build()); // Retrieving the file. @@ -121,6 +124,10 @@ public void s3ImplementationTest() throws DecoderException { fail("The file should now exist"); } + // Testing file deletion. + assertTrue(contentService.fileExists("test.txt")); + contentService.deleteFile("test.txt"); + assertFalse(contentService.fileExists("test.txt")); } @BeforeAll diff --git a/backend/explorer-core/autoconfigure/src/test/resources/application.properties b/backend/explorer-core/autoconfigure/src/test/resources/application.properties index 6a010af5d..3b1d2f4a5 100644 --- a/backend/explorer-core/autoconfigure/src/test/resources/application.properties +++ b/backend/explorer-core/autoconfigure/src/test/resources/application.properties @@ -5,7 +5,7 @@ # ====================================================================================================================== # Database access configuration. spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver -spring.datasource.url=jdbc:hsqldb:mem:cassandre-database;DB_CLOSE_DELAY=-1 +spring.datasource.url=jdbc:hsqldb:mem:explorer-royllo-database spring.datasource.username=sa spring.datasource.password= # diff --git a/backend/servers/explorer-api/src/main/java/org/royllo/explorer/api/configuration/SecurityConfiguration.java b/backend/servers/explorer-api/src/main/java/org/royllo/explorer/api/configuration/SecurityConfiguration.java new file mode 100644 index 000000000..4dd212f1f --- /dev/null +++ b/backend/servers/explorer-api/src/main/java/org/royllo/explorer/api/configuration/SecurityConfiguration.java @@ -0,0 +1,35 @@ +package org.royllo.explorer.api.configuration; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.stereotype.Controller; + +import static org.springframework.security.config.http.SessionCreationPolicy.NEVER; +import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; + +/** + * Security configuration. + */ +@Controller +@EnableWebSecurity +@RequiredArgsConstructor +@SuppressWarnings({"checkstyle:DesignForExtension"}) +public class SecurityConfiguration { + + @Bean + public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception { + // Each request that comes in passes through this chain of filters before reaching your application. + return http.csrf(AbstractHttpConfigurer::disable) + .cors(AbstractHttpConfigurer::disable) + .sessionManagement(session -> session.sessionCreationPolicy(NEVER)) + // Page authorisations. + .authorizeHttpRequests((authorize) -> authorize + .requestMatchers(antMatcher("/**")).permitAll() + ).build(); + } + +} diff --git a/backend/servers/explorer-api/src/main/resources/application.properties b/backend/servers/explorer-api/src/main/resources/application.properties index bb824a0a7..6708717c1 100644 --- a/backend/servers/explorer-api/src/main/resources/application.properties +++ b/backend/servers/explorer-api/src/main/resources/application.properties @@ -5,7 +5,7 @@ # ====================================================================================================================== # Database access configuration. spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver -spring.datasource.url=jdbc:hsqldb:mem:cassandre-database;DB_CLOSE_DELAY=-1 +spring.datasource.url=jdbc:hsqldb:mem:explorer-royllo-database spring.datasource.username=sa spring.datasource.password= # diff --git a/backend/servers/explorer-batch/src/main/resources/application.properties b/backend/servers/explorer-batch/src/main/resources/application.properties index e1ee0aff9..59f6718ff 100644 --- a/backend/servers/explorer-batch/src/main/resources/application.properties +++ b/backend/servers/explorer-batch/src/main/resources/application.properties @@ -5,7 +5,7 @@ # ====================================================================================================================== # Database access configuration. spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver -spring.datasource.url=jdbc:hsqldb:mem:cassandre-database;DB_CLOSE_DELAY=-1 +spring.datasource.url=jdbc:hsqldb:mem:explorer-royllo-database spring.datasource.username=sa spring.datasource.password= # diff --git a/backend/servers/explorer-web/README.md b/backend/servers/explorer-web/README.md index ca117bda3..6d0204dea 100644 --- a/backend/servers/explorer-web/README.md +++ b/backend/servers/explorer-web/README.md @@ -8,10 +8,30 @@ Server side: Client side: `npm run build && npm run watch` -## Design choices +## Lnurl Auth -- [HTML first for the philosophy](https://html-first.com/). -- [Tailwindcss as CSS framework](https://tailwindcss.com/). -- [Tailwind components library](https://daisyui.com/). -- [Tailwind dynamic components with Aline](https://devdojo.com/pines) -- [Example design](https://tailwindui.com/components/ecommerce/page-examples/shopping-cart-pages). +In order for lnurl-auth to work you must serve your app over `https` (no self-signed cert allowed). + +Serving your app with `https` during development can be done with [ngrok](https://ngrok.com/): + +```bash +./ngrok http 8080 +``` + +This will return a public url that you can use to access your app over `https`: + +```bash +Session Status online +Account stephane.traumat@gmail.com (Plan: Free) +Version 3.5.0 +Region Europe (eu) +Latency 22ms +Web Interface http://127.0.0.1:4040 +Forwarding https://348a-2001-861-5300-9e20-bf7e-7941-39d8-1e6d.ngrok-free.app -> http://localhost:8080 +``` + +You can then use the `https` url to access your app by updating `application-dev.properties`, change +to `royllo.explorer.web.base-url=https://348a-2001-861-5300-9e20-bf7e-7941-39d8-1e6d.ngrok-free.app` (without the +trailing `/`). + +Then run `mvn spring-boot:run -Dspring-boot.run.profiles=dev`.s \ No newline at end of file diff --git a/backend/servers/explorer-web/pom.xml b/backend/servers/explorer-web/pom.xml index b97e899a2..264d2313d 100644 --- a/backend/servers/explorer-web/pom.xml +++ b/backend/servers/explorer-web/pom.xml @@ -34,10 +34,19 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.session + spring-session-jdbc + + org.springframework.boot spring-boot-starter-thymeleaf + + org.thymeleaf.extras + thymeleaf-extras-springsecurity6 + nz.net.ultraq.thymeleaf thymeleaf-layout-dialect diff --git a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/ErrorConfiguration.java b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/ErrorConfiguration.java index 79862a782..ea157ac72 100644 --- a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/ErrorConfiguration.java +++ b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/ErrorConfiguration.java @@ -2,6 +2,7 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; +import org.royllo.explorer.core.util.base.Base; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -18,7 +19,7 @@ */ @Controller @RequiredArgsConstructor -public class ErrorConfiguration implements ErrorController { +public class ErrorConfiguration extends Base implements ErrorController { /** * Handle error. @@ -32,6 +33,7 @@ public String handleError(final HttpServletRequest request) { if (status != null) { int statusCode = Integer.parseInt(status.toString()); if (statusCode == NOT_FOUND.value()) { + logger.error("Error 404: Page not found: {}", request.getRequestURI()); return ERROR_404_PAGE; } if (statusCode == INTERNAL_SERVER_ERROR.value()) { diff --git a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/LnurlAuthConfiguration.java b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/LnurlAuthConfiguration.java new file mode 100644 index 000000000..ed48c38aa --- /dev/null +++ b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/LnurlAuthConfiguration.java @@ -0,0 +1,60 @@ +package org.royllo.explorer.web.configuration; + +import jakarta.servlet.ServletContext; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.royllo.explorer.core.repository.util.K1ValueRepository; +import org.royllo.explorer.core.service.util.DatabaseK1Manager; +import org.royllo.explorer.core.util.parameters.RoylloExplorerParameters; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.tbk.lnurl.auth.K1Manager; +import org.tbk.lnurl.auth.LnurlAuthFactory; +import org.tbk.lnurl.auth.SimpleLnurlAuthFactory; + +import java.net.URI; +import java.net.URISyntaxException; + +import static org.royllo.explorer.web.util.constants.AuthenticationPageConstants.LNURL_AUTH_WALLET_LOGIN_PATH; + +/** + * Configuration for LNURL-auth. + */ +@Configuration +@RequiredArgsConstructor +@SuppressWarnings({"checkstyle:DesignForExtension"}) +public class LnurlAuthConfiguration { + + /** Royllo explorer parameters. */ + private final RoylloExplorerParameters roylloExplorerParameters; + + /** K1 value repository. */ + private final K1ValueRepository k1ValueRepository; + + /** + * K1 manager (managed thanks to a database table). + * "k1" refers to a one-time, randomly generated key or token. + * + * @return k1 manager + */ + @Bean + K1Manager k1Manager() { + return new DatabaseK1Manager(k1ValueRepository); + } + + /** + * This factory creates a LNURL-auth callback url for the given k1. + * + * @param k1Manager k1 manager + * @param servletContext servlet context + * @return lnurl auth factory + */ + @Bean + @SneakyThrows(URISyntaxException.class) + LnurlAuthFactory lnurlAuthFactory(final K1Manager k1Manager, + final ServletContext servletContext) { + URI callbackUrl = new URI(roylloExplorerParameters.getWeb().getBaseUrl() + servletContext.getContextPath() + LNURL_AUTH_WALLET_LOGIN_PATH); + return new SimpleLnurlAuthFactory(callbackUrl, k1Manager); + } + +} diff --git a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/SecurityConfiguration.java b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/SecurityConfiguration.java new file mode 100644 index 000000000..225252139 --- /dev/null +++ b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/SecurityConfiguration.java @@ -0,0 +1,121 @@ +package org.royllo.explorer.web.configuration; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.stereotype.Controller; +import org.tbk.lnurl.auth.K1Manager; +import org.tbk.lnurl.auth.LnurlAuthFactory; +import org.tbk.lnurl.auth.LnurlAuthPairingService; +import org.tbk.spring.lnurl.security.LnurlAuthConfigurer; + +import static org.royllo.explorer.web.util.constants.AuthenticationPageConstants.LNURL_AUTH_LOGIN_PAGE_PATH; +import static org.royllo.explorer.web.util.constants.AuthenticationPageConstants.LNURL_AUTH_SESSION_K1_KEY; +import static org.royllo.explorer.web.util.constants.AuthenticationPageConstants.LNURL_AUTH_SESSION_LOGIN_PATH; +import static org.royllo.explorer.web.util.constants.AuthenticationPageConstants.LNURL_AUTH_WALLET_LOGIN_PATH; +import static org.springframework.security.config.http.SessionCreationPolicy.NEVER; +import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; + +/** + * Security configuration. + * TODO remove @Controller + */ +@Controller +@EnableWebSecurity +@RequiredArgsConstructor +@SuppressWarnings({"checkstyle:DesignForExtension"}) +public class SecurityConfiguration { + + /** LNURL Auth factory. */ + private final LnurlAuthFactory lnurlAuthFactory; + + /** K1 manager. "k1" refers to a one-time, randomly generated key or token. */ + private final K1Manager lnurlAuthk1Manager; + + /** This service acts like a user detail service for LNURL-auth. */ + private final LnurlAuthPairingService lnurlAuthPairingService; + + /** User details service. */ + private final UserDetailsService userDetailsService; + + @Bean + public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception { + // Each request that comes in passes through this chain of filters before reaching your application. + return http + // TODO Is it necessary to disable CSRF and CORS protection? + .csrf(AbstractHttpConfigurer::disable) + .cors(AbstractHttpConfigurer::disable) + // Specifying that the application should not create new HTTP sessions on its own, but can use existing ones if they are present. + // Additionally, it protects against session fixation attacks by migrating the session (i.e., changing the session ID) upon authentication. + .sessionManagement(session -> session + .sessionCreationPolicy(NEVER) + .sessionFixation().migrateSession() + ) + // Page authorisations. + .authorizeHttpRequests((authorize) -> authorize + // Public website. + .requestMatchers( + // Pages. + antMatcher("/"), + antMatcher("/search"), + antMatcher("/asset/**"), + antMatcher("/request/**"), + // Login pages + // TODO Try to remove those lines. + antMatcher(LNURL_AUTH_LOGIN_PAGE_PATH + "/**"), + antMatcher(LNURL_AUTH_WALLET_LOGIN_PATH + "/**"), + antMatcher("/api/v1/lnurl-auth/login/**"), + // CSS, images and javascript libraries. + antMatcher("/css/**"), + antMatcher("/images/**"), + antMatcher("/svg/**"), + antMatcher("/favicon/**"), + antMatcher("/webjars/**"), + // Util. + antMatcher("/sitemap.xml") + ).permitAll() + // User account pages (Requires authorizations). + .requestMatchers( + antMatcher("/account/**") + ).authenticated() + ) + // If the user is not authenticated when accessing a protected page, redirect to the login page. +// .exceptionHandling((exceptionHandling) -> exceptionHandling +// .accessDeniedPage(ERROR_403_PAGE) +// .authenticationEntryPoint((request, response, authenticationException) -> response.sendRedirect(LNURL_AUTH_LOGIN_PAGE_PATH)) +// ) + // LNURL Auth configuration. + .with(new LnurlAuthConfigurer(), lnurlAuthConfigurer -> + lnurlAuthConfigurer + // Specify the services we built. + .k1Manager(lnurlAuthk1Manager) + .pairingService(lnurlAuthPairingService) + .lnurlAuthFactory(lnurlAuthFactory) + .authenticationUserDetailsService(userDetailsService) + // Set the login page endpoint. + .loginPageEndpoint(login -> login + .enable(true) + .baseUri(LNURL_AUTH_LOGIN_PAGE_PATH) + ) + // Configures the LNURL Authorization Server's Session Endpoint. + .sessionEndpoint(session -> session + .baseUri(LNURL_AUTH_SESSION_LOGIN_PATH) + .sessionK1Key(LNURL_AUTH_SESSION_K1_KEY) + .successHandlerCustomizer(successHandler -> { + successHandler.setDefaultTargetUrl("/"); + successHandler.setTargetUrlParameter("redirect"); + successHandler.setAlwaysUseDefaultTargetUrl(false); + successHandler.setUseReferer(false); + }) + ) + // Configures the LNURL Authorization Server's Wallet Endpoint. + .walletEndpoint(wallet -> wallet.baseUri(LNURL_AUTH_WALLET_LOGIN_PATH)) + ) + .build(); + } + +} diff --git a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/WebConfiguration.java b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/WebConfiguration.java index e96975613..ae2bf5f25 100644 --- a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/WebConfiguration.java +++ b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/configuration/WebConfiguration.java @@ -23,7 +23,6 @@ @RequiredArgsConstructor public class WebConfiguration implements WebMvcConfigurer { - /** Assets search results default page size. */ public static final int ASSET_SEARCH_DEFAULT_PAGE_SIZE = 10; diff --git a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/advice/ConfigurationControllerAdvice.java b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/advice/ConfigurationControllerAdvice.java index f6de4f850..199d72171 100644 --- a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/advice/ConfigurationControllerAdvice.java +++ b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/advice/ConfigurationControllerAdvice.java @@ -28,13 +28,14 @@ public class ConfigurationControllerAdvice { @ModelAttribute public final void handleRequest(final Model model) { - // Set the base url for web, API and assets. + // Set the base url for web, api and contents. model.addAttribute(API_BASE_URL_ATTRIBUTE, roylloExplorerParameters.getApi().getBaseUrl()); model.addAttribute(WEB_BASE_URL_ATTRIBUTE, roylloExplorerParameters.getWeb().getBaseUrl()); model.addAttribute(CONTENT_BASE_URL_ATTRIBUTE, roylloExplorerParameters.getContent().getBaseUrl()); // Set the analytics parameters. model.addAttribute(PIWIK_ANALYTICS_TRACKING_ID_ATTRIBUTE, roylloExplorerAnalyticsParameters.getPiwik().getTrackingId()); + } } diff --git a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/constants/AuthenticationPageConstants.java b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/constants/AuthenticationPageConstants.java new file mode 100644 index 000000000..cb2f7018b --- /dev/null +++ b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/constants/AuthenticationPageConstants.java @@ -0,0 +1,24 @@ +package org.royllo.explorer.web.util.constants; + +import lombok.experimental.UtilityClass; + +/** + * Authentication page constants. + */ +@UtilityClass +@SuppressWarnings("checkstyle:HideUtilityClassConstructor") +public class AuthenticationPageConstants { + + /** The path to the login page. */ + public static final String LNURL_AUTH_LOGIN_PAGE_PATH = "/login"; + + /** The path to the auth wallet login page. */ + public static final String LNURL_AUTH_WALLET_LOGIN_PATH = "/api/v1/lnurl-auth/login/wallet"; + + /** The path to the auth session login page. */ + public static final String LNURL_AUTH_SESSION_LOGIN_PATH = "/api/v1/lnurl-auth/login/session?redirect=/"; + + /** Authentication session K1 key. */ + public static final String LNURL_AUTH_SESSION_K1_KEY = "k1"; + +} diff --git a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/constants/UtilPagesConstants.java b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/constants/UtilPagesConstants.java index 2470b3876..33f5760bc 100644 --- a/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/constants/UtilPagesConstants.java +++ b/backend/servers/explorer-web/src/main/java/org/royllo/explorer/web/util/constants/UtilPagesConstants.java @@ -15,6 +15,9 @@ public class UtilPagesConstants { /** Generic error page. */ public static final String ERROR_PAGE = "util/errors/error"; + /** Error 403 page. */ + public static final String ERROR_403_PAGE = "/error/403"; + /** Error 404 page. */ public static final String ERROR_404_PAGE = "util/errors/error-404"; diff --git a/backend/servers/explorer-web/src/main/resources/application-dev.properties b/backend/servers/explorer-web/src/main/resources/application-dev.properties index acbdd4ef3..fc527b7e2 100644 --- a/backend/servers/explorer-web/src/main/resources/application-dev.properties +++ b/backend/servers/explorer-web/src/main/resources/application-dev.properties @@ -1,7 +1,7 @@ # ====================================================================================================================== # URL for dev royllo.explorer.api.base-url=http://localhost:9090/api -royllo.explorer.web.base-url=http://localhost:3000 +royllo.explorer.web.base-url=https://7daf-2001-861-5300-9e20-c520-117c-aa9a-d9b7.ngrok-free.app royllo.explorer.content.base-url=http://localhost:9093 # # ====================================================================================================================== @@ -9,6 +9,11 @@ royllo.explorer.content.base-url=http://localhost:9093 royllo.explorer.analytics.piwik.trackingId= # # ====================================================================================================================== +# Session configuration +spring.session.store-type=jdbc +spring.session.jdbc.initialize-schema=always +# +# ====================================================================================================================== # Using Liquibase to import test data spring.liquibase.enabled=true spring.liquibase.change-log=db/dev/db.dev-data.yaml @@ -16,4 +21,15 @@ spring.liquibase.change-log=db/dev/db.dev-data.yaml # ====================================================================================================================== # Disable cache during local dev spring.thymeleaf.cache=false -spring.web.resources.chain.cache=false \ No newline at end of file +spring.web.resources.chain.cache=false +# +# Enable SQL logging +#spring.jpa.show-sql=true +# Set logging level for Spring Session JDBC +#logging.level.org.springframework.session.jdbc=DEBUG +# If you want to see the SQL statements in the console +#logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG +#logging.level.org.hibernate.SQL=DEBUG +logging.config:classpath:logback.xml +logging.level.org.springframework:INFO +logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping:TRACE \ No newline at end of file diff --git a/backend/servers/explorer-web/src/main/resources/application.properties b/backend/servers/explorer-web/src/main/resources/application.properties index 2fc4b846e..5f840be8f 100644 --- a/backend/servers/explorer-web/src/main/resources/application.properties +++ b/backend/servers/explorer-web/src/main/resources/application.properties @@ -15,7 +15,7 @@ royllo.explorer.analytics.piwik.trackingId=00000000-0000-0000-0000-000000000000 # ====================================================================================================================== # Database access configuration. spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver -spring.datasource.url=jdbc:hsqldb:mem:cassandre-database;DB_CLOSE_DELAY=-1 +spring.datasource.url=jdbc:hsqldb:mem:explorer-royllo-database spring.datasource.username=sa spring.datasource.password= # @@ -27,6 +27,11 @@ s3.bucket-name=royllo-explorer-s3-asset-bucket s3.endpoint-url=https://s3.amazonaws.com # # ====================================================================================================================== +# Session configuration +spring.session.store-type=jdbc +spring.session.jdbc.initialize-schema=always +# +# ====================================================================================================================== # Cache configuration. spring.cache.type=caffeine # diff --git a/backend/servers/explorer-web/src/main/resources/logback.xml b/backend/servers/explorer-web/src/main/resources/logback.xml new file mode 100644 index 000000000..4bbffe165 --- /dev/null +++ b/backend/servers/explorer-web/src/main/resources/logback.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/backend/servers/explorer-web/src/main/resources/templates/util/fragments/layouts/default/header.html b/backend/servers/explorer-web/src/main/resources/templates/util/fragments/layouts/default/header.html index 32b3a6e89..6ab7397b1 100644 --- a/backend/servers/explorer-web/src/main/resources/templates/util/fragments/layouts/default/header.html +++ b/backend/servers/explorer-web/src/main/resources/templates/util/fragments/layouts/default/header.html @@ -4,10 +4,7 @@
- + Royllo Logo
+
+ User authenticated +
+ +   +   +   +