From c2dd6e8a4a95ac3d57df354652b60d6c2f61b8dd Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Sun, 22 Oct 2023 20:15:43 -0400 Subject: [PATCH] Added NodeInitializer to replace specific individual initializers. --- .../com/andrewlalis/onyx/NodeInitializer.java | 68 +++++++++++++++++++ .../onyx/auth/service/TokenService.java | 2 +- .../onyx/config/SecurityComponents.java | 14 ++++ .../onyx/content/ContentTreeInitializer.java | 30 -------- .../model/access/ContentAccessRules.java | 3 +- .../application-development.properties | 2 +- 6 files changed, 86 insertions(+), 33 deletions(-) create mode 100644 onyx-api/src/main/java/com/andrewlalis/onyx/NodeInitializer.java create mode 100644 onyx-api/src/main/java/com/andrewlalis/onyx/config/SecurityComponents.java delete mode 100644 onyx-api/src/main/java/com/andrewlalis/onyx/content/ContentTreeInitializer.java diff --git a/onyx-api/src/main/java/com/andrewlalis/onyx/NodeInitializer.java b/onyx-api/src/main/java/com/andrewlalis/onyx/NodeInitializer.java new file mode 100644 index 0000000..965b676 --- /dev/null +++ b/onyx-api/src/main/java/com/andrewlalis/onyx/NodeInitializer.java @@ -0,0 +1,68 @@ +package com.andrewlalis.onyx; + +import com.andrewlalis.onyx.auth.model.User; +import com.andrewlalis.onyx.auth.model.UserRepository; +import com.andrewlalis.onyx.content.dao.ContentNodeRepository; +import com.andrewlalis.onyx.content.model.ContentContainerNode; +import com.andrewlalis.onyx.content.model.ContentNode; +import com.andrewlalis.onyx.content.model.access.ContentAccessLevel; +import com.andrewlalis.onyx.content.model.access.UserContentAccessRule; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +/** + * A component that runs on application startup, to take care of some + * first-time initialization, if needed: + *
    + *
  1. Create a default admin user, if none exists.
  2. + *
  3. Create the root content node for the content tree.
  4. + *
+ */ +@Component +@RequiredArgsConstructor +@Slf4j +public class NodeInitializer implements CommandLineRunner { + private final ContentNodeRepository contentNodeRepository; + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + + @Override + public void run(String... args) throws Exception { + User adminUser = createAdminUserIfNoUsers(); + createRootNodeIfNotExists(adminUser); + } + + private void createRootNodeIfNotExists(User adminUser) { + if (!contentNodeRepository.existsByName(ContentNode.ROOT_NODE_NAME)) { + ContentNode rootNode = new ContentContainerNode(ContentNode.ROOT_NODE_NAME, null); + rootNode.getAccessInfo().setPublicAccessLevel(ContentAccessLevel.NONE); + rootNode.getAccessInfo().setNetworkAccessLevel(ContentAccessLevel.NONE); + rootNode.getAccessInfo().setNodeAccessLevel(ContentAccessLevel.NONE); + if (adminUser != null) { + rootNode.getAccessInfo().getUserAccessRules().add(new UserContentAccessRule( + adminUser, + rootNode.getAccessInfo(), + ContentAccessLevel.EDIT + )); + } + contentNodeRepository.saveAndFlush(rootNode); + log.info("Created content root container node."); + } + } + + private User createAdminUserIfNoUsers() { + if (userRepository.count() == 0) { + User user = userRepository.saveAndFlush(new User( + "admin", + "Admin", + passwordEncoder.encode("onyx-admin") + )); + log.info("Created user \"admin\" with password \"onyx-admin\""); + return user; + } + return null; + } +} diff --git a/onyx-api/src/main/java/com/andrewlalis/onyx/auth/service/TokenService.java b/onyx-api/src/main/java/com/andrewlalis/onyx/auth/service/TokenService.java index 29bcd91..a9f88f6 100644 --- a/onyx-api/src/main/java/com/andrewlalis/onyx/auth/service/TokenService.java +++ b/onyx-api/src/main/java/com/andrewlalis/onyx/auth/service/TokenService.java @@ -40,7 +40,7 @@ public class TokenService { private static final String ISSUER = "Onyx API"; private PrivateKey signingKey; - private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(10); + private final PasswordEncoder passwordEncoder; private final RefreshTokenRepository refreshTokenRepository; private final UserRepository userRepository; diff --git a/onyx-api/src/main/java/com/andrewlalis/onyx/config/SecurityComponents.java b/onyx-api/src/main/java/com/andrewlalis/onyx/config/SecurityComponents.java new file mode 100644 index 0000000..1b7c29d --- /dev/null +++ b/onyx-api/src/main/java/com/andrewlalis/onyx/config/SecurityComponents.java @@ -0,0 +1,14 @@ +package com.andrewlalis.onyx.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class SecurityComponents { + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(10); + } +} diff --git a/onyx-api/src/main/java/com/andrewlalis/onyx/content/ContentTreeInitializer.java b/onyx-api/src/main/java/com/andrewlalis/onyx/content/ContentTreeInitializer.java deleted file mode 100644 index 45354a8..0000000 --- a/onyx-api/src/main/java/com/andrewlalis/onyx/content/ContentTreeInitializer.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.andrewlalis.onyx.content; - -import com.andrewlalis.onyx.content.dao.ContentNodeRepository; -import com.andrewlalis.onyx.content.model.access.ContentAccessLevel; -import com.andrewlalis.onyx.content.model.ContentContainerNode; -import com.andrewlalis.onyx.content.model.ContentNode; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.CommandLineRunner; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -@Slf4j -public class ContentTreeInitializer implements CommandLineRunner { - private final ContentNodeRepository contentNodeRepository; - - @Override - public void run(String... args) throws Exception { - log.info("Check if content root container node exists."); - if (!contentNodeRepository.existsByName(ContentNode.ROOT_NODE_NAME)) { - ContentNode rootNode = new ContentContainerNode(ContentNode.ROOT_NODE_NAME, null); - rootNode.getAccessInfo().setPublicAccessLevel(ContentAccessLevel.NONE); - rootNode.getAccessInfo().setNetworkAccessLevel(ContentAccessLevel.NONE); - rootNode.getAccessInfo().setNodeAccessLevel(ContentAccessLevel.NONE); - contentNodeRepository.saveAndFlush(rootNode); - log.info("Created content root container node."); - } - } -} diff --git a/onyx-api/src/main/java/com/andrewlalis/onyx/content/model/access/ContentAccessRules.java b/onyx-api/src/main/java/com/andrewlalis/onyx/content/model/access/ContentAccessRules.java index fb63599..3d7be3c 100644 --- a/onyx-api/src/main/java/com/andrewlalis/onyx/content/model/access/ContentAccessRules.java +++ b/onyx-api/src/main/java/com/andrewlalis/onyx/content/model/access/ContentAccessRules.java @@ -5,6 +5,7 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; +import java.util.HashSet; import java.util.Set; /** @@ -40,5 +41,5 @@ public class ContentAccessRules { * User-specific access rules that override other more generic rules, if present. */ @OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = CascadeType.ALL, mappedBy = "contentAccessRules") - private Set userAccessRules; + private Set userAccessRules = new HashSet<>(); } diff --git a/onyx-api/src/main/resources/application-development.properties b/onyx-api/src/main/resources/application-development.properties index a684ac0..b64b8f5 100644 --- a/onyx-api/src/main/resources/application-development.properties +++ b/onyx-api/src/main/resources/application-development.properties @@ -2,4 +2,4 @@ spring.datasource.url=jdbc:h2:./onyx-db-dev spring.datasource.driver-class-name=org.h2.Driver -spring.jpa.hibernate.ddl-auto=create +spring.jpa.hibernate.ddl-auto=update