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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
6 changes: 6 additions & 0 deletions classGPT/backend/.gitignore → backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ build/

### VS Code ###
.vscode/

/target/
/data/
.idea/
.gradle/
.DS_Store
File renamed without changes.
File renamed without changes.
28 changes: 26 additions & 2 deletions classGPT/backend/pom.xml → backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,42 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.3</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>

</dependencies>

<build>
Expand Down
78 changes: 78 additions & 0 deletions backend/src/main/java/io/ATTTT/classGPT/config/SeedData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.ATTTT.classGPT.config;

import io.ATTTT.classGPT.models.Account;
import io.ATTTT.classGPT.models.Authority;
import io.ATTTT.classGPT.models.Post;
import io.ATTTT.classGPT.repositories.AuthorityRepository;
import io.ATTTT.classGPT.services.AccountService;
import io.ATTTT.classGPT.services.PostService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Component
@RequiredArgsConstructor
public class SeedData implements CommandLineRunner {

private final PostService postService;
private final AccountService accountService;
private final AuthorityRepository authorityRepository;

@Override
public void run(String...args) throws Exception{
List<Post> posts = postService.getAll();

if(posts.isEmpty()){

Authority user = new Authority();
user.setName("ROLE_USER");
authorityRepository.save(user);

Authority admin = new Authority();
admin.setName("ROLE_ADMIN");
authorityRepository.save(admin);

Account account1 = new Account();
Account account2 = new Account();

account1.setFirstName("user");
account1.setLastName("user");
account1.setEmail("user.user@domain.com");
account1.setPassword("password");
Set<Authority> authorities1 = new HashSet<>();
authorityRepository.findById("ROLE_USER").ifPresent(authorities1::add);
account1.setAuthorities(authorities1);


account2.setFirstName("admin");
account2.setLastName("admin");
account2.setEmail("admin.admin@domain.com");
account2.setPassword("password");
Set<Authority> authorities2 = new HashSet<>();
authorityRepository.findById("ROLE_USER").ifPresent(authorities2::add);
authorityRepository.findById("ROLE_ADMIN").ifPresent(authorities2::add);
account2.setAuthorities(authorities2);

accountService.save(account1);
accountService.save(account2);

Post post1 = new Post();
post1.setTitle("title of post 1");
post1.setBody("This is the body of post 1");
post1.setAccount(account1);

Post post2 = new Post();
post2.setTitle("title of post 2");
post2.setBody("This is the body of post 2");
post2.setAccount(account2);

postService.save(post1);
postService.save(post2);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.ATTTT.classGPT.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

import jakarta.servlet.http.HttpServletResponse;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig {

@Bean
public static PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}


@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(withDefaults())
.csrf(csrf -> csrf.disable())


.authorizeHttpRequests(auth -> auth
.requestMatchers("api/auth/register").permitAll()
.requestMatchers(HttpMethod.GET, "/login").permitAll()
.requestMatchers(HttpMethod.GET, "api/posts/**").permitAll()
.anyRequest().authenticated()
)
// login config
.formLogin(form -> form
.loginProcessingUrl("/login")
.usernameParameter("email")
.passwordParameter("password")
.successHandler((request, response, authentication) -> {
response.setStatus(HttpServletResponse.SC_OK);
})
.failureHandler((request, response, exception) -> {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
})
.permitAll()
)
// logout config
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessHandler((request, response, authentication) -> {
response.setStatus(HttpServletResponse.SC_OK);
})
.permitAll()
)
// basic auth (optional; for APIs, etc.)
.httpBasic(httpBasic -> {});

return http.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package io.ATTTT.classGPT.controllers;

import io.ATTTT.classGPT.models.Account;
import io.ATTTT.classGPT.models.Authority;
import io.ATTTT.classGPT.services.AccountService;
import io.ATTTT.classGPT.repositories.AuthorityRepository;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {

private final AccountService accountService;
private final PasswordEncoder passwordEncoder;
private final AuthorityRepository authorityRepository;

@PostMapping("/register")
public ResponseEntity<Account> register(@RequestBody RegisterRequest req) {
Account account = new Account();

account.setEmail(req.getEmail());
account.setPassword(req.getPassword());

String roleName = "ROLE_USER";
if ("instructor".equalsIgnoreCase(req.getRole())) {
roleName = "ROLE_ADMIN";
}

Set<Authority> authorities = new HashSet<>();
authorityRepository.findById(roleName).ifPresent(authorities::add);
account.setAuthorities(authorities);

String fullName = req.getFullName() != null ? req.getFullName().trim() : "";
String firstName = fullName;
String lastName = "";
int space = fullName.indexOf(' ');
if (space > 0) {
firstName = fullName.substring(0, space);
lastName = fullName.substring(space + 1);
}

account.setFirstName(firstName);
account.setLastName(lastName);

Account saved = accountService.save(account);


return ResponseEntity.ok(saved);
}

@Data
public static class RegisterRequest {
private String fullName;
private String email;
private String password;
private String role;
private List<String> classCodes;
}

@Data
public static class AccountRequest {
private Long id;
private String fullName;
private String email;
private String password;
private String role;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package io.ATTTT.classGPT.controllers;

import io.ATTTT.classGPT.models.Account;
import io.ATTTT.classGPT.models.Post;
import io.ATTTT.classGPT.services.AccountService;
import io.ATTTT.classGPT.services.PostService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.security.Principal;
import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/api/posts")
@RequiredArgsConstructor
public class PostController {

private static final Logger log = LoggerFactory.getLogger(PostController.class);

private final PostService postService;
private final AccountService accountService;


@GetMapping
public List<Post> getAllPosts() {
return postService.getAll();
}


@GetMapping("/{id}")
public ResponseEntity<Post> getPost(@PathVariable Long id){
return postService.getById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

@PostMapping
@PreAuthorize("isAuthenticated()")
public ResponseEntity<Post> createPost(@RequestBody Post incoming,
Principal principal) {
String email = principal.getName();
Account account = accountService.findByEmail(email)
.orElseThrow(() -> new IllegalArgumentException("Account not found"));

Post post = new Post();
post.setTitle(incoming.getTitle());
post.setBody(incoming.getBody());
post.setAccount(account);

Post saved = postService.save(post);
return ResponseEntity.ok(saved);
}


@PutMapping("/{id}")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<Post> updatePost(@PathVariable Long id,
@RequestBody Post incoming) {

return postService.getById(id)
.map(existing -> {
existing.setTitle(incoming.getTitle());
existing.setBody(incoming.getBody());
Post saved = postService.save(existing);
return ResponseEntity.ok(saved);
})
.orElse(ResponseEntity.notFound().build());
}


@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Object> deletePost(@PathVariable Long id) {
return postService.getById(id)
.map(post -> {
postService.delete(post);
return ResponseEntity.noContent().build();
})
.orElse(ResponseEntity.notFound().build());
}
}
Loading