commit
43faab805b
@ -0,0 +1,38 @@ |
|||||||
|
target/ |
||||||
|
!.mvn/wrapper/maven-wrapper.jar |
||||||
|
!**/src/main/**/target/ |
||||||
|
!**/src/test/**/target/ |
||||||
|
|
||||||
|
### IntelliJ IDEA ### |
||||||
|
.idea/modules.xml |
||||||
|
.idea/jarRepositories.xml |
||||||
|
.idea/compiler.xml |
||||||
|
.idea/libraries/ |
||||||
|
*.iws |
||||||
|
*.iml |
||||||
|
*.ipr |
||||||
|
|
||||||
|
### Eclipse ### |
||||||
|
.apt_generated |
||||||
|
.classpath |
||||||
|
.factorypath |
||||||
|
.project |
||||||
|
.settings |
||||||
|
.springBeans |
||||||
|
.sts4-cache |
||||||
|
|
||||||
|
### NetBeans ### |
||||||
|
/nbproject/private/ |
||||||
|
/nbbuild/ |
||||||
|
/dist/ |
||||||
|
/nbdist/ |
||||||
|
/.nb-gradle/ |
||||||
|
build/ |
||||||
|
!**/src/main/**/build/ |
||||||
|
!**/src/test/**/build/ |
||||||
|
|
||||||
|
### VS Code ### |
||||||
|
.vscode/ |
||||||
|
|
||||||
|
### Mac OS ### |
||||||
|
.DS_Store |
@ -0,0 +1,8 @@ |
|||||||
|
# Default ignored files |
||||||
|
/shelf/ |
||||||
|
/workspace.xml |
||||||
|
# Editor-based HTTP Client requests |
||||||
|
/httpRequests/ |
||||||
|
# Datasource local storage ignored files |
||||||
|
/dataSources/ |
||||||
|
/dataSources.local.xml |
@ -0,0 +1,7 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project version="4"> |
||||||
|
<component name="Encoding"> |
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" /> |
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" /> |
||||||
|
</component> |
||||||
|
</project> |
@ -0,0 +1,14 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project version="4"> |
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" /> |
||||||
|
<component name="MavenProjectsManager"> |
||||||
|
<option name="originalFiles"> |
||||||
|
<list> |
||||||
|
<option value="$PROJECT_DIR$/pom.xml" /> |
||||||
|
</list> |
||||||
|
</option> |
||||||
|
</component> |
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK"> |
||||||
|
<output url="file://$PROJECT_DIR$/out" /> |
||||||
|
</component> |
||||||
|
</project> |
@ -0,0 +1,6 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project version="4"> |
||||||
|
<component name="VcsDirectoryMappings"> |
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" /> |
||||||
|
</component> |
||||||
|
</project> |
@ -0,0 +1,85 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
|
||||||
|
<groupId>org.example</groupId> |
||||||
|
<artifactId>demoNeo4j</artifactId> |
||||||
|
<version>1.0-SNAPSHOT</version> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<java.version>17</java.version> |
||||||
|
<maven.compiler.source>${java.version}</maven.compiler.source> |
||||||
|
<maven.compiler.target>${java.version}</maven.compiler.target> |
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencyManagement> |
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-dependencies</artifactId> |
||||||
|
<version>3.0.0</version> |
||||||
|
<type>pom</type> |
||||||
|
<scope>import</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
</dependencyManagement> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-web</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-data-neo4j</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.neo4j.driver</groupId> |
||||||
|
<artifactId>neo4j-java-driver</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-devtools</artifactId> |
||||||
|
<scope>runtime</scope> |
||||||
|
<optional>true</optional> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-test</artifactId> |
||||||
|
<scope>test</scope> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-security</artifactId> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>me.paulschwarz</groupId> |
||||||
|
<artifactId>spring-dotenv</artifactId> |
||||||
|
<version>3.0.0</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework</groupId> |
||||||
|
<artifactId>spring-jdbc</artifactId> |
||||||
|
<version>6.1.8</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.projectlombok</groupId> |
||||||
|
<artifactId>lombok</artifactId> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
</dependencies> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
</project> |
@ -0,0 +1,11 @@ |
|||||||
|
package org.example; |
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication; |
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||||
|
|
||||||
|
@SpringBootApplication |
||||||
|
public class Main { |
||||||
|
public static void main(String[] args) { |
||||||
|
SpringApplication.run(Main.class, args); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package org.example.config; |
||||||
|
|
||||||
|
import org.example.services.NeoUserDetailsService; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.security.config.Customizer; |
||||||
|
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.config.http.SessionCreationPolicy; |
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder; |
||||||
|
import org.springframework.security.web.SecurityFilterChain; |
||||||
|
import org.springframework.web.cors.CorsConfiguration; |
||||||
|
import org.springframework.web.cors.CorsConfigurationSource; |
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
|
||||||
|
@Configuration |
||||||
|
@EnableWebSecurity |
||||||
|
public class SecurityConfig { |
||||||
|
private final NeoUserDetailsService neoUserDetailsService; |
||||||
|
|
||||||
|
public SecurityConfig(NeoUserDetailsService neoUserDetailsService) { |
||||||
|
this.neoUserDetailsService = neoUserDetailsService; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { |
||||||
|
return httpSecurity |
||||||
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) |
||||||
|
.csrf(AbstractHttpConfigurer::disable) |
||||||
|
.cors(Customizer.withDefaults()) |
||||||
|
.authorizeHttpRequests(auth -> auth |
||||||
|
.requestMatchers( |
||||||
|
"/api/v1/auth/me" |
||||||
|
).authenticated() |
||||||
|
.anyRequest().permitAll() |
||||||
|
) |
||||||
|
.userDetailsService(neoUserDetailsService) |
||||||
|
.httpBasic(Customizer.withDefaults()) |
||||||
|
.build(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
PasswordEncoder passwordEncoder() { |
||||||
|
return new BCryptPasswordEncoder(); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
CorsConfigurationSource corsConfigurationSource() { |
||||||
|
CorsConfiguration configuration = new CorsConfiguration(); |
||||||
|
|
||||||
|
// TODO: make sure that the origin list comes from an environment file.
|
||||||
|
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3001", "http://127.0.0.1:3000")); |
||||||
|
configuration.setAllowedMethods(Arrays.asList("GET","POST","PATCH", "PUT", "DELETE", "OPTIONS", "HEAD")); |
||||||
|
configuration.setAllowCredentials(true); |
||||||
|
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Requestor-Type", "Content-Type")); |
||||||
|
configuration.setExposedHeaders(Arrays.asList("X-Get-Header")); |
||||||
|
configuration.setMaxAge(3600L); |
||||||
|
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); |
||||||
|
source.registerCorsConfiguration("/**", configuration); |
||||||
|
|
||||||
|
return source; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package org.example.controllers; |
||||||
|
|
||||||
|
import org.example.models.User; |
||||||
|
import org.example.objects.UserDTO; |
||||||
|
import org.example.requests.CreateUserRequest; |
||||||
|
import org.example.services.UserService; |
||||||
|
import org.springframework.http.HttpStatus; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
|
import org.springframework.web.bind.annotation.*; |
||||||
|
|
||||||
|
import java.security.Principal; |
||||||
|
@RestController |
||||||
|
@RequestMapping("/api/v1/auth") |
||||||
|
public class UserController { |
||||||
|
private final UserService userService; |
||||||
|
|
||||||
|
public UserController(UserService userService) { |
||||||
|
this.userService = userService; |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("/me") |
||||||
|
public String loggedInUserDetails(Principal principal) { |
||||||
|
return principal.getName(); |
||||||
|
} |
||||||
|
|
||||||
|
@PostMapping("/register") |
||||||
|
public ResponseEntity<UserDTO> signUp(@RequestBody CreateUserRequest request) { |
||||||
|
User user = userService.createUser(request); |
||||||
|
|
||||||
|
UserDTO responseUser = new UserDTO(user.getName(), user.getUsername(), user.getRoles()); |
||||||
|
|
||||||
|
return new ResponseEntity<>(responseUser, HttpStatus.CREATED); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,98 @@ |
|||||||
|
package org.example.models; |
||||||
|
|
||||||
|
import org.springframework.data.neo4j.core.schema.GeneratedValue; |
||||||
|
import org.springframework.data.neo4j.core.schema.Id; |
||||||
|
import org.springframework.data.neo4j.core.schema.Node; |
||||||
|
import org.springframework.security.core.GrantedAuthority; |
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority; |
||||||
|
import org.springframework.security.core.userdetails.UserDetails; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Collection; |
||||||
|
|
||||||
|
@Node |
||||||
|
public class User implements UserDetails { |
||||||
|
@Id @GeneratedValue |
||||||
|
private Long id; |
||||||
|
private String name; |
||||||
|
private String username; |
||||||
|
private String password; |
||||||
|
private String roles; |
||||||
|
|
||||||
|
public User() { |
||||||
|
} |
||||||
|
|
||||||
|
public Long getId() { |
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getUsername() { |
||||||
|
return username; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getPassword() { |
||||||
|
return password; |
||||||
|
} |
||||||
|
|
||||||
|
public String getRoles() { |
||||||
|
return roles; |
||||||
|
} |
||||||
|
|
||||||
|
public void setName(String name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public void setUsername(String username) { |
||||||
|
this.username = username; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPassword(String password) { |
||||||
|
this.password = password; |
||||||
|
} |
||||||
|
|
||||||
|
public void setRoles(String roles) { |
||||||
|
this.roles = roles; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isAccountNonExpired() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isAccountNonLocked() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isCredentialsNonExpired() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEnabled() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() { |
||||||
|
return Arrays.stream(roles.split(",")) |
||||||
|
.map(SimpleGrantedAuthority::new) |
||||||
|
.toList(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "User{" + |
||||||
|
"name='" + name + '\'' + |
||||||
|
", username='" + username + '\'' + |
||||||
|
", roles='" + roles + '\'' + |
||||||
|
'}'; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package org.example.objects; |
||||||
|
|
||||||
|
public class UserDTO { |
||||||
|
private String name; |
||||||
|
private String username; |
||||||
|
private String roles; |
||||||
|
|
||||||
|
public UserDTO(String name, String username, String roles) { |
||||||
|
this.name = name; |
||||||
|
this.username = username; |
||||||
|
this.roles = roles; |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
public void setName(String name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
public String getUsername() { |
||||||
|
return username; |
||||||
|
} |
||||||
|
public void setUsername(String username) { |
||||||
|
this.username = username; |
||||||
|
} |
||||||
|
public String getRoles() { |
||||||
|
return roles; |
||||||
|
} |
||||||
|
public void setRoles(String roles) { |
||||||
|
this.roles = roles; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package org.example.queryresults; |
||||||
|
|
||||||
|
|
||||||
|
import org.example.models.User; |
||||||
|
|
||||||
|
public class CourseEnrolmentQueryResult { |
||||||
|
private User user; |
||||||
|
|
||||||
|
public CourseEnrolmentQueryResult() { |
||||||
|
} |
||||||
|
|
||||||
|
public User getUser() { |
||||||
|
return user; |
||||||
|
} |
||||||
|
|
||||||
|
public void setUser(User user) { |
||||||
|
this.user = user; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package org.example.repositories; |
||||||
|
|
||||||
|
import org.example.models.User; |
||||||
|
import org.example.queryresults.CourseEnrolmentQueryResult; |
||||||
|
import org.springframework.data.neo4j.repository.Neo4jRepository; |
||||||
|
import org.springframework.data.neo4j.repository.query.Query; |
||||||
|
|
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
public interface UserRepository extends Neo4jRepository<User, Long> { |
||||||
|
Optional<User> findUserByUsername(String username); |
||||||
|
|
||||||
|
@Query("MATCH (user:User), (course:Course) WHERE user.username = $username AND course.identifier = $identifier " + |
||||||
|
"RETURN EXISTS((user)-[:ENROLLED_IN]->(course))") |
||||||
|
Boolean findEnrolmentStatus(String username, String identifier); |
||||||
|
|
||||||
|
@Query("MATCH (user:User), (course:Course) WHERE user.username = $username AND course.identifier = $identifier " + |
||||||
|
"CREATE (user)-[:ENROLLED_IN]->(course) RETURN user, course") |
||||||
|
CourseEnrolmentQueryResult createEnrolmentRelationship(String username, String identifier); |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
package org.example.requests; |
||||||
|
|
||||||
|
public class CreateUserRequest { |
||||||
|
private String name; |
||||||
|
private String username; |
||||||
|
private String password; |
||||||
|
private String roles; |
||||||
|
|
||||||
|
public CreateUserRequest() { |
||||||
|
} |
||||||
|
public CreateUserRequest(String name, String username, String password, String roles) { |
||||||
|
this.name = name; |
||||||
|
this.username = username; |
||||||
|
this.password = password; |
||||||
|
this.roles = roles; |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public void setName(String name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public String getUsername() { |
||||||
|
return username; |
||||||
|
} |
||||||
|
|
||||||
|
public void setUsername(String username) { |
||||||
|
this.username = username; |
||||||
|
} |
||||||
|
|
||||||
|
public String getPassword() { |
||||||
|
return password; |
||||||
|
} |
||||||
|
|
||||||
|
public void setPassword(String password) { |
||||||
|
this.password = password; |
||||||
|
} |
||||||
|
|
||||||
|
public String getRoles() { |
||||||
|
return roles; |
||||||
|
} |
||||||
|
|
||||||
|
public void setRoles(String roles) { |
||||||
|
this.roles = roles; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package org.example.services; |
||||||
|
|
||||||
|
import org.example.repositories.UserRepository; |
||||||
|
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; |
||||||
|
|
||||||
|
@Service |
||||||
|
public class NeoUserDetailsService implements UserDetailsService { |
||||||
|
|
||||||
|
private final UserRepository userRepository; |
||||||
|
|
||||||
|
public NeoUserDetailsService(UserRepository userRepository) { |
||||||
|
this.userRepository = userRepository; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { |
||||||
|
return userRepository |
||||||
|
.findUserByUsername(username) |
||||||
|
.orElseThrow(() -> new UsernameNotFoundException("Username not found" + username)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package org.example.services; |
||||||
|
|
||||||
|
import org.example.models.User; |
||||||
|
import org.example.repositories.UserRepository; |
||||||
|
import org.example.requests.CreateUserRequest; |
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
@Service |
||||||
|
public class UserService { |
||||||
|
private final UserRepository userRepository; |
||||||
|
|
||||||
|
private final PasswordEncoder passwordEncoder; |
||||||
|
|
||||||
|
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) { |
||||||
|
this.userRepository = userRepository; |
||||||
|
this.passwordEncoder = passwordEncoder; |
||||||
|
} |
||||||
|
|
||||||
|
public User createUser(CreateUserRequest request) { |
||||||
|
User user = new User(); |
||||||
|
|
||||||
|
user.setName(request.getName()); |
||||||
|
// TODO: make sure that this username doesn't exist.
|
||||||
|
user.setUsername(request.getUsername()); |
||||||
|
user.setRoles(request.getRoles()); |
||||||
|
user.setPassword(passwordEncoder.encode(request.getPassword())); |
||||||
|
|
||||||
|
userRepository.save(user); |
||||||
|
|
||||||
|
return user; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
spring.neo4j.uri=neo4j://localhost:7687 |
||||||
|
spring.neo4j.authentication.username=neo4j |
||||||
|
spring.neo4j.authentication.password=12345678 |
@ -0,0 +1 @@ |
|||||||
|
Subproject commit 81ad3d154b3b36b98dc5c1179c7e7bfcd7a0b1b0 |
Loading…
Reference in new issue