From a7c5e1758871fe073dcd548bbf2e22e16b5493ca Mon Sep 17 00:00:00 2001 From: azmenezi Date: Thu, 10 Apr 2025 18:27:23 +0300 Subject: [PATCH 1/5] Abdulaziz Alenezi, hello world task --- .../com/coded/spring/ordering/OrdersController.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/kotlin/com/coded/spring/ordering/OrdersController.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt new file mode 100644 index 0000000..8bc26d0 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt @@ -0,0 +1,11 @@ +package com.coded.spring.ordering + +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +class HelloWorldController(){ + + @GetMapping("/hello") + fun helloWorld() = "Hello World" +} \ No newline at end of file From 2ae4343cbd851cefcaafdf595e8e77e419dd418f Mon Sep 17 00:00:00 2001 From: azmenezi Date: Thu, 10 Apr 2025 18:44:46 +0300 Subject: [PATCH 2/5] Abdulaziz Alenezi, task two done --- pom.xml | 12 ++++++++++ .../coded/spring/ordering/OrdersController.kt | 23 ++++++++++++++++--- .../coded/spring/ordering/OrdersRepository.kt | 22 ++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt diff --git a/pom.xml b/pom.xml index 163ad53..aafb459 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,18 @@ kotlin-test-junit5 test + + jakarta.inject + jakarta.inject-api + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt index 8bc26d0..af7ffb7 100644 --- a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt @@ -1,11 +1,28 @@ package com.coded.spring.ordering import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RestController @RestController -class HelloWorldController(){ +class HelloWorldController( + val ordersRepository: OrdersRepository +) { @GetMapping("/hello") - fun helloWorld() = "Hello World" -} \ No newline at end of file + fun helloWorld() = "Hello World" + + @PostMapping("/orders") + fun submitOrder(@RequestBody request: OrderRequest) { + ordersRepository.save(OrderEntity(restaurant = request.restaurant, items = request.items)) + } + + @GetMapping("/orders") + fun getOrders(): MutableList = ordersRepository.findAll() +} + +data class OrderRequest( + val restaurant: String, + val items: List +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt new file mode 100644 index 0000000..0f589fe --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt @@ -0,0 +1,22 @@ +package com.coded.spring.ordering + + +import jakarta.inject.Named +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface OrdersRepository : JpaRepository + + +@Entity +@Table(name = "orders") +data class OrderEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + val restaurant: String, + val items: List +) { + constructor() : this(null, "", listOf("")) +} From 92ddf0fd6d6ca264fc8029ef38f639f00e4475dd Mon Sep 17 00:00:00 2001 From: azmenezi Date: Sun, 13 Apr 2025 17:50:05 +0300 Subject: [PATCH 3/5] Abdulaziz Alenezi, task 3 --- .../coded/spring/ordering/ItemsRepository.kt | 25 ++++++++++++++++ .../coded/spring/ordering/OrdersController.kt | 30 ++++++++++++++----- .../coded/spring/ordering/OrdersRepository.kt | 16 +++++++--- 3 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/ordering/ItemsRepository.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/ItemsRepository.kt b/src/main/kotlin/com/coded/spring/ordering/ItemsRepository.kt new file mode 100644 index 0000000..00fd802 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/ItemsRepository.kt @@ -0,0 +1,25 @@ + +package com.coded.spring.ordering + + +import jakarta.inject.Named +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface ItemsRepository : JpaRepository { + fun findAllByNameIn(names: List): List +} + + +@Entity +@Table(name = "items") +data class ItemsEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + val name: String, + +) { + constructor() : this(null, "") +} diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt index af7ffb7..7f0b395 100644 --- a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt @@ -1,13 +1,11 @@ package com.coded.spring.ordering -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.RequestBody -import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.* @RestController class HelloWorldController( - val ordersRepository: OrdersRepository + val ordersRepository: OrdersRepository, + val itemsRepository: ItemsRepository ) { @GetMapping("/hello") @@ -15,9 +13,21 @@ class HelloWorldController( @PostMapping("/orders") fun submitOrder(@RequestBody request: OrderRequest) { - ordersRepository.save(OrderEntity(restaurant = request.restaurant, items = request.items)) + val items = itemsRepository.findAllByNameIn(request.items) + + val order = OrderEntity(restaurant = request.restaurant, items = items) + ordersRepository.save(order) + } + + @PostMapping("/items") + fun createItems(@RequestBody request: CreateItemsRequest): List { + val items = request.items.map { ItemsEntity(name = it) } + return itemsRepository.saveAll(items) } - + + + + @GetMapping("/orders") fun getOrders(): MutableList = ordersRepository.findAll() } @@ -25,4 +35,8 @@ class HelloWorldController( data class OrderRequest( val restaurant: String, val items: List -) \ No newline at end of file +) + +data class CreateItemsRequest( + val items: List +) diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt index 0f589fe..a58c553 100644 --- a/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt @@ -8,15 +8,23 @@ import org.springframework.data.jpa.repository.JpaRepository @Named interface OrdersRepository : JpaRepository - @Entity @Table(name = "orders") data class OrderEntity( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null, + val restaurant: String, - val items: List -) { - constructor() : this(null, "", listOf("")) + + @ManyToMany + @JoinTable( + name = "order_items", // This is the join table + joinColumns = [JoinColumn(name = "order_id")], + inverseJoinColumns = [JoinColumn(name = "item_id")] + ) + val items: List = emptyList() +) + { + constructor() : this(null, "", listOf(ItemsEntity())) } From 6bea06314944ba9e8ee5075343df9e70386d6c80 Mon Sep 17 00:00:00 2001 From: azmenezi Date: Sun, 13 Apr 2025 18:34:49 +0300 Subject: [PATCH 4/5] Abdulaziz Alenezi, task 3 --- pom.xml | 4 ++-- src/main/resources/application.properties | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aafb459..4bc2809 100644 --- a/pom.xml +++ b/pom.xml @@ -67,8 +67,8 @@ spring-boot-starter-data-jpa - com.h2database - h2 + org.postgresql + postgresql diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3704dc6..e55b719 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,6 @@ spring.application.name=Kotlin.SpringbootV2 + +spring.datasource.url=jdbc:postgresql://localhost:5433/myHelloDatabase +spring.datasource.username=postgres +spring.datasource.password=184481 +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file From 41bb561f0f59dad534d81803a0ae1214322e046e Mon Sep 17 00:00:00 2001 From: azmenezi Date: Sun, 20 Apr 2025 19:21:13 +0300 Subject: [PATCH 5/5] Abdulaziz Alenezi --- pom.xml | 34 ++++++++--- .../coded/spring/ordering/InitUserRunner.kt | 35 +++++++++++ .../coded/spring/ordering/ItemsRepository.kt | 25 -------- .../coded/spring/ordering/OrdersController.kt | 42 ------------- .../coded/spring/ordering/OrdersRepository.kt | 30 ---------- .../CustomUserDetailsService.kt | 21 +++++++ .../ordering/authentication/SecurityConfig.kt | 58 ++++++++++++++++++ .../jwt/AuthenticationController.kt | 60 +++++++++++++++++++ .../jwt/JwtAuthenticationFilter.kt | 45 ++++++++++++++ .../ordering/authentication/jwt/JwtService.kt | 42 +++++++++++++ .../controllers/HelloWorldController.kt | 25 ++++++++ .../ordering/controllers/OrdersController.kt | 33 ++++++++++ .../ordering/controllers/UsersController.kt | 14 +++++ .../spring/ordering/entities/ItemEntity.kt | 17 ++++++ .../spring/ordering/entities/OrderEntity.kt | 27 +++++++++ .../spring/ordering/entities/UserEntity.kt | 25 ++++++++ .../ordering/repositories/ItemsRepository.kt | 7 +++ .../ordering/repositories/OrdersRepository.kt | 9 +++ .../ordering/repositories/UsersRepository.kt | 16 +++++ .../spring/ordering/services/OrdersService.kt | 29 +++++++++ .../spring/ordering/services/UsersService.kt | 22 +++++++ 21 files changed, 512 insertions(+), 104 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/ordering/InitUserRunner.kt delete mode 100644 src/main/kotlin/com/coded/spring/ordering/ItemsRepository.kt delete mode 100644 src/main/kotlin/com/coded/spring/ordering/OrdersController.kt delete mode 100644 src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/authentication/jwt/AuthenticationController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtAuthenticationFilter.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtService.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/controllers/HelloWorldController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/controllers/OrdersController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/controllers/UsersController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/entities/ItemEntity.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/entities/OrderEntity.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/entities/UserEntity.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/repositories/ItemsRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/repositories/OrdersRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/repositories/UsersRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/services/OrdersService.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/services/UsersService.kt diff --git a/pom.xml b/pom.xml index 4bc2809..f89a153 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,18 @@ org.jetbrains.kotlin kotlin-stdlib - + + jakarta.inject + jakarta.inject-api + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.postgresql + postgresql + org.springframework.boot spring-boot-starter-test @@ -59,16 +70,25 @@ test - jakarta.inject - jakarta.inject-api + io.jsonwebtoken + jjwt-api + 0.11.5 - org.springframework.boot - spring-boot-starter-data-jpa + io.jsonwebtoken + jjwt-impl + runtime + 0.11.5 - org.postgresql - postgresql + io.jsonwebtoken + jjwt-jackson + runtime + 0.11.5 + + + org.springframework.boot + spring-boot-starter-security diff --git a/src/main/kotlin/com/coded/spring/ordering/InitUserRunner.kt b/src/main/kotlin/com/coded/spring/ordering/InitUserRunner.kt new file mode 100644 index 0000000..68d54d6 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/InitUserRunner.kt @@ -0,0 +1,35 @@ +package com.coded.spring.ordering + +import com.coded.spring.ordering.entities.Roles +import com.coded.spring.ordering.entities.UserEntity +import com.coded.spring.ordering.repositories.UsersRepository + +import org.springframework.boot.CommandLineRunner +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.context.annotation.Bean +import org.springframework.security.crypto.password.PasswordEncoder + +@SpringBootApplication +class InitUserRunner { + @Bean + fun initUsers(userRepository: UsersRepository, passwordEncoder: PasswordEncoder) = CommandLineRunner { + val user = UserEntity( + name = "HelloUser", + username = "testuser", + password = passwordEncoder.encode("password123"), + age = 18, + role = Roles.USER + ) + if (userRepository.findByUsername(user.username) == null) { + println("Creating user ${user.username}") + userRepository.save(user) + } else { + println("User ${user.username} already exists") + } + } +} + +fun main(args: Array) { + runApplication(*args).close() +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/ItemsRepository.kt b/src/main/kotlin/com/coded/spring/ordering/ItemsRepository.kt deleted file mode 100644 index 00fd802..0000000 --- a/src/main/kotlin/com/coded/spring/ordering/ItemsRepository.kt +++ /dev/null @@ -1,25 +0,0 @@ - -package com.coded.spring.ordering - - -import jakarta.inject.Named -import jakarta.persistence.* -import org.springframework.data.jpa.repository.JpaRepository - -@Named -interface ItemsRepository : JpaRepository { - fun findAllByNameIn(names: List): List -} - - -@Entity -@Table(name = "items") -data class ItemsEntity( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - var id: Long? = null, - val name: String, - -) { - constructor() : this(null, "") -} diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt deleted file mode 100644 index 7f0b395..0000000 --- a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.coded.spring.ordering - -import org.springframework.web.bind.annotation.* - -@RestController -class HelloWorldController( - val ordersRepository: OrdersRepository, - val itemsRepository: ItemsRepository -) { - - @GetMapping("/hello") - fun helloWorld() = "Hello World" - - @PostMapping("/orders") - fun submitOrder(@RequestBody request: OrderRequest) { - val items = itemsRepository.findAllByNameIn(request.items) - - val order = OrderEntity(restaurant = request.restaurant, items = items) - ordersRepository.save(order) - } - - @PostMapping("/items") - fun createItems(@RequestBody request: CreateItemsRequest): List { - val items = request.items.map { ItemsEntity(name = it) } - return itemsRepository.saveAll(items) - } - - - - - @GetMapping("/orders") - fun getOrders(): MutableList = ordersRepository.findAll() -} - -data class OrderRequest( - val restaurant: String, - val items: List -) - -data class CreateItemsRequest( - val items: List -) diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt deleted file mode 100644 index a58c553..0000000 --- a/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.coded.spring.ordering - - -import jakarta.inject.Named -import jakarta.persistence.* -import org.springframework.data.jpa.repository.JpaRepository - -@Named -interface OrdersRepository : JpaRepository - -@Entity -@Table(name = "orders") -data class OrderEntity( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - var id: Long? = null, - - val restaurant: String, - - @ManyToMany - @JoinTable( - name = "order_items", // This is the join table - joinColumns = [JoinColumn(name = "order_id")], - inverseJoinColumns = [JoinColumn(name = "item_id")] - ) - val items: List = emptyList() -) - { - constructor() : this(null, "", listOf(ItemsEntity())) -} diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt new file mode 100644 index 0000000..7f652db --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/CustomUserDetailsService.kt @@ -0,0 +1,21 @@ +package com.coded.spring.ordering.authentication + +import com.coded.spring.ordering.repositories.UsersRepository +import org.springframework.security.core.userdetails.* +import org.springframework.stereotype.Service + +@Service +class CustomUserDetailsService( + private val usersRepository: UsersRepository +) : UserDetailsService { + override fun loadUserByUsername(username: String): UserDetails { + val user = usersRepository.findByUsername(username) + ?: throw UsernameNotFoundException("User not found") + + return User.builder() + .username(user.username) + .password(user.password) + .roles(user.role.toString()) + .build() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt new file mode 100644 index 0000000..a4880e1 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/SecurityConfig.kt @@ -0,0 +1,58 @@ +package com.coded.spring.ordering.authentication + + +import com.coded.spring.ordering.authentication.jwt.JwtAuthenticationFilter +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.authentication.AuthenticationProvider +import org.springframework.security.authentication.dao.DaoAuthenticationProvider +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.security.web.SecurityFilterChain +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter + + +@Configuration +@EnableWebSecurity +class SecurityConfig( + private val jwtAuthFilter: JwtAuthenticationFilter, + private val userDetailsService: UserDetailsService +) { + + @Bean + fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + http.csrf { it.disable() } + .authorizeHttpRequests { + it.requestMatchers("/auth/**").permitAll() + .anyRequest().authenticated() + } + .sessionManagement { + it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + } + .authenticationProvider(authenticationProvider()) + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter::class.java) + + return http.build() + } + + @Bean + fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder() + + @Bean + fun authenticationManager(config: AuthenticationConfiguration): AuthenticationManager = + config.authenticationManager + + @Bean + fun authenticationProvider(): AuthenticationProvider { + val provider = DaoAuthenticationProvider() + provider.setUserDetailsService(userDetailsService) + provider.setPasswordEncoder(passwordEncoder()) + return provider + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/AuthenticationController.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/AuthenticationController.kt new file mode 100644 index 0000000..0440b75 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/AuthenticationController.kt @@ -0,0 +1,60 @@ +package com.coded.spring.ordering.authentication.jwt + +import org.springframework.security.authentication.* +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.core.userdetails.UsernameNotFoundException +import org.springframework.web.bind.annotation.* + + +@RestController +@RequestMapping("/auth") +class AuthenticationController( + private val authenticationManager: AuthenticationManager, + private val userDetailsService: UserDetailsService, + private val jwtService: JwtService +) { + + @GetMapping("/test") + fun testing() = "test" + + @PostMapping("/login") + fun login(@RequestBody authRequest: AuthenticationRequest): AuthenticationResponse { + println("🔐 Received login request: $authRequest") + + val authToken = UsernamePasswordAuthenticationToken(authRequest.username, authRequest.password) + println("🔐 Created UsernamePasswordAuthenticationToken") + + val authentication = try { + authenticationManager.authenticate(authToken) + } catch (ex: Exception) { + println("❌ Authentication failed: ${ex.message}") + throw UsernameNotFoundException("Invalid credentials") + } + + println("✅ Authentication success: ${authentication.isAuthenticated}") + println("➡️ Authorities: ${authentication.authorities}") + + if (authentication.isAuthenticated) { + val userDetails = userDetailsService.loadUserByUsername(authRequest.username) + println("✅ Loaded user details for: ${userDetails.username}") + + val token = jwtService.generateToken(userDetails.username) + println("🔑 Generated JWT token: $token") + + return AuthenticationResponse(token) + } else { + println("❌ Authentication marked as not authenticated") + throw UsernameNotFoundException("Invalid user request!") + } + + } +} + +data class AuthenticationRequest( + val username: String, + val password: String +) + +data class AuthenticationResponse( + val token: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtAuthenticationFilter.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtAuthenticationFilter.kt new file mode 100644 index 0000000..f180b7c --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtAuthenticationFilter.kt @@ -0,0 +1,45 @@ +package com.coded.spring.ordering.authentication.jwt + +import jakarta.servlet.FilterChain +import jakarta.servlet.http.* +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter + +@Component +class JwtAuthenticationFilter( + private val jwtService: JwtService, + private val userDetailsService: UserDetailsService +) : OncePerRequestFilter() { + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + val authHeader = request.getHeader("Authorization") + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response) + return + } + + val token = authHeader.substring(7) + val username = jwtService.extractUsername(token) + + if (SecurityContextHolder.getContext().authentication == null) { + if (jwtService.isTokenValid(token, username)) { + val userDetails = userDetailsService.loadUserByUsername(username) + val authToken = UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.authorities + ) + authToken.details = WebAuthenticationDetailsSource().buildDetails(request) + SecurityContextHolder.getContext().authentication = authToken + } + } + + filterChain.doFilter(request, response) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtService.kt b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtService.kt new file mode 100644 index 0000000..c877c7c --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/authentication/jwt/JwtService.kt @@ -0,0 +1,42 @@ +package com.coded.spring.ordering.authentication.jwt + +import io.jsonwebtoken.* +import io.jsonwebtoken.security.Keys +import org.springframework.stereotype.Component +import java.util.* +import javax.crypto.SecretKey + +@Component +class JwtService { + + private val secretKey: SecretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256) + private val expirationMs: Long = 1000 * 60 * 60 + + fun generateToken(username: String): String { + val now = Date() + val expiry = Date(now.time + expirationMs) + + return Jwts.builder() + .setSubject(username) + .setIssuedAt(now) + .setExpiration(expiry) + .signWith(secretKey) + .compact() + } + + fun extractUsername(token: String): String = + Jwts.parserBuilder() + .setSigningKey(secretKey) + .build() + .parseClaimsJws(token) + .body + .subject + + fun isTokenValid(token: String, username: String): Boolean { + return try { + extractUsername(token) == username + } catch (e: Exception) { + false + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/controllers/HelloWorldController.kt b/src/main/kotlin/com/coded/spring/ordering/controllers/HelloWorldController.kt new file mode 100644 index 0000000..3fa2e3f --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/controllers/HelloWorldController.kt @@ -0,0 +1,25 @@ +package com.coded.spring.ordering.controllers + +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + +@RestController +class HelloWorldController(){ + + @GetMapping("/hello") + fun helloWorld() = "Hello World" + + @GetMapping("/boo") + fun testing(): String { + return "AAAA" + } + + @PostMapping("/hello") + fun helloName(@RequestBody req: Name) = "Hello ${req.name}!" +} + +data class Name( + val name:String +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/controllers/OrdersController.kt b/src/main/kotlin/com/coded/spring/ordering/controllers/OrdersController.kt new file mode 100644 index 0000000..5efe5d1 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/controllers/OrdersController.kt @@ -0,0 +1,33 @@ +package com.coded.spring.ordering.controllers + +import com.coded.spring.ordering.entities.ItemEntity +import com.coded.spring.ordering.entities.OrderEntity +import com.coded.spring.ordering.repositories.OrdersRepository +import com.coded.spring.ordering.services.OrdersService +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + + +@RestController + +class OrdersController(val ordersRepository: OrdersRepository, val ordersService: OrdersService) { + + @PostMapping("/orders") + fun createNewOrder(@RequestBody req: OrderRequest) { + ordersService.createOrder(userId = req.user) + } + + @GetMapping("/orders") + fun getOrders(): MutableList { + return ordersRepository.findAll() + } + + + data class OrderRequest( + val user: Long, + val restaurant: String, + val items: List + ) +} diff --git a/src/main/kotlin/com/coded/spring/ordering/controllers/UsersController.kt b/src/main/kotlin/com/coded/spring/ordering/controllers/UsersController.kt new file mode 100644 index 0000000..59ea37e --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/controllers/UsersController.kt @@ -0,0 +1,14 @@ +package com.coded.spring.ordering.controllers + +import com.coded.spring.ordering.services.UsersService +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +class UsersController( + private val usersService: UsersService +){ + + @GetMapping("/users/v1/list") + fun users() = usersService.listUsers() +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/entities/ItemEntity.kt b/src/main/kotlin/com/coded/spring/ordering/entities/ItemEntity.kt new file mode 100644 index 0000000..db854c8 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/entities/ItemEntity.kt @@ -0,0 +1,17 @@ +package com.coded.spring.ordering.entities + +import jakarta.persistence.* + + +@Entity +@Table(name="items") +data class ItemEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + val name: String, + val quantity: Int, + val order_id: Long +){ + constructor() : this(null, "", 0, 0) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/entities/OrderEntity.kt b/src/main/kotlin/com/coded/spring/ordering/entities/OrderEntity.kt new file mode 100644 index 0000000..12ef48c --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/entities/OrderEntity.kt @@ -0,0 +1,27 @@ +package com.coded.spring.ordering.entities + +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.ManyToOne +import jakarta.persistence.OneToMany +import jakarta.persistence.Table + +@Entity +@Table(name = "orders") +data class OrderEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + val restaurant:String? =null, + + @ManyToOne + val user: UserEntity, + + + @OneToMany(mappedBy = "order_id") + val items: List? = null +){ + constructor() : this(null,null, UserEntity(), listOf()) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/entities/UserEntity.kt b/src/main/kotlin/com/coded/spring/ordering/entities/UserEntity.kt new file mode 100644 index 0000000..a593071 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/entities/UserEntity.kt @@ -0,0 +1,25 @@ +package com.coded.spring.ordering.entities + +import jakarta.persistence.* + +@Entity +@Table(name = "users") +data class UserEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + var name: String, + var age: Int, + var username: String, + var password: String, + + @Enumerated(EnumType.STRING) + val role: Roles = Roles.USER + +) { + constructor() : this(0, "name", 0, "username", "password", Roles.USER) +} + +enum class Roles { + USER, ADMIN +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/repositories/ItemsRepository.kt b/src/main/kotlin/com/coded/spring/ordering/repositories/ItemsRepository.kt new file mode 100644 index 0000000..ccafe63 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/repositories/ItemsRepository.kt @@ -0,0 +1,7 @@ +package com.coded.spring.ordering.repositories + +import com.coded.spring.ordering.entities.ItemEntity +import org.springframework.data.jpa.repository.JpaRepository + + +interface ItemsRepository: JpaRepository diff --git a/src/main/kotlin/com/coded/spring/ordering/repositories/OrdersRepository.kt b/src/main/kotlin/com/coded/spring/ordering/repositories/OrdersRepository.kt new file mode 100644 index 0000000..f2a4d81 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/repositories/OrdersRepository.kt @@ -0,0 +1,9 @@ +package com.coded.spring.ordering.repositories + +import com.coded.spring.ordering.entities.OrderEntity +import jakarta.inject.Named +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface OrdersRepository : JpaRepository + diff --git a/src/main/kotlin/com/coded/spring/ordering/repositories/UsersRepository.kt b/src/main/kotlin/com/coded/spring/ordering/repositories/UsersRepository.kt new file mode 100644 index 0000000..8db0783 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/repositories/UsersRepository.kt @@ -0,0 +1,16 @@ +package com.coded.spring.ordering.repositories + +import com.coded.spring.ordering.entities.UserEntity +import jakarta.inject.Named +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface UsersRepository : JpaRepository { + fun age(age: Int): MutableList + + fun findByUsername(username: String): UserEntity? + +} + + + diff --git a/src/main/kotlin/com/coded/spring/ordering/services/OrdersService.kt b/src/main/kotlin/com/coded/spring/ordering/services/OrdersService.kt new file mode 100644 index 0000000..2c80d27 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/services/OrdersService.kt @@ -0,0 +1,29 @@ +package com.coded.spring.ordering.services + +import com.coded.spring.ordering.entities.ItemEntity +import com.coded.spring.ordering.entities.OrderEntity +import com.coded.spring.ordering.entities.UserEntity +import com.coded.spring.ordering.repositories.OrdersRepository +import com.coded.spring.ordering.repositories.UsersRepository +import jakarta.inject.Named + +@Named +class OrdersService( + private val usersRepository: UsersRepository, + private val orderRepository: OrdersRepository +) { + + + fun createOrder(userId: Long): OrderResponse { + val user = usersRepository.findById(userId).get() + val newOrder = OrderEntity(user = user) + orderRepository.save(newOrder) + return OrderResponse(newOrder.user, newOrder.items) + } + +} + +data class OrderResponse( + val user: UserEntity, + val items: List? +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/services/UsersService.kt b/src/main/kotlin/com/coded/spring/ordering/services/UsersService.kt new file mode 100644 index 0000000..2627eae --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/services/UsersService.kt @@ -0,0 +1,22 @@ +package com.coded.spring.ordering.services + +import com.coded.spring.ordering.repositories.UsersRepository +import jakarta.inject.Named + +@Named +class UsersService( + private val usersRepository: UsersRepository, +) { + + fun listUsers(): List = usersRepository.findAll().map { + UserResponse( + name = it.name, + age = it.age + ) + } +} + +data class UserResponse( + val name: String, + val age: Int +) \ No newline at end of file