From 3514bb7d2d207ac936f61cdb1283d391da9994cb Mon Sep 17 00:00:00 2001 From: Mishal Alquraini Date: Tue, 8 Apr 2025 18:32:31 +0300 Subject: [PATCH 01/10] done --- pom.xml | 12 ++++++ .../spring/ordering/OnlineOrderRepository.kt | 23 ++++++++++ .../ordering/OnlineOrderingController.kt | 42 +++++++++++++++++++ src/main/resources/application.properties | 1 + 4 files changed, 78 insertions(+) create mode 100644 src/main/kotlin/com/coded/spring/ordering/OnlineOrderRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/OnlineOrderingController.kt diff --git a/pom.xml b/pom.xml index 163ad53..be47663 100644 --- a/pom.xml +++ b/pom.xml @@ -53,6 +53,18 @@ spring-boot-starter-test test + + jakarta.inject + jakarta.inject-api + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + org.jetbrains.kotlin kotlin-test-junit5 diff --git a/src/main/kotlin/com/coded/spring/ordering/OnlineOrderRepository.kt b/src/main/kotlin/com/coded/spring/ordering/OnlineOrderRepository.kt new file mode 100644 index 0000000..3cb405c --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/OnlineOrderRepository.kt @@ -0,0 +1,23 @@ +package com.coded.spring.ordering + +import jakarta.inject.Named +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface OrderRepository : JpaRepository + +@Entity +@Table(name = "usernames") +data class Order( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + @Column(name="username") + val username: String, + val resturant: String, + @CollectionTable + val items: MutableList +){ + constructor() : this(null,"", "", mutableListOf()) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/OnlineOrderingController.kt b/src/main/kotlin/com/coded/spring/ordering/OnlineOrderingController.kt new file mode 100644 index 0000000..9ee3476 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/OnlineOrderingController.kt @@ -0,0 +1,42 @@ +package com.coded.spring.ordering + +import org.springframework.boot.autoconfigure.security.SecurityProperties +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody + +@RestController +class OnlineOrderingController( + val orderRepository: OrderRepository +){ + + @GetMapping("/helloOrders") + fun hello() = + orderRepository.findAll() + + + @PostMapping("/orders") + // this is the command + fun addOrder( @RequestBody request: OrderRequest){ + val addUser = Order( + username = request.user, + resturant = request.restaurant, + items = request.items + ) + } + @PostMapping("/orders1") + // this is the same command but we can save it (better) + fun saveOrders( @RequestBody request: OrderRequest) = orderRepository.save( + Order(username = request.user, + resturant = request.restaurant, + items = request.items) + ) + +} + +data class OrderRequest( + val user: String , + val restaurant: String , + val items: MutableList +) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3704dc6..57a5e62 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,2 @@ spring.application.name=Kotlin.SpringbootV2 +server.port = 8081 \ No newline at end of file From 289d4dd0bd5f5dec0ffcda62dab9209d5814a90c Mon Sep 17 00:00:00 2001 From: Mishal Alquraini Date: Thu, 10 Apr 2025 20:44:21 +0300 Subject: [PATCH 02/10] finished post order + saved in DB (did the challenge as well) --- pom.xml | 4 +- .../spring/{ordering => }/Application.kt | 2 +- .../spring/ordering/OnlineOrderRepository.kt | 23 ---------- .../ordering/OnlineOrderingController.kt | 42 ------------------- .../coded/spring/ordering/OrdersController.kt | 31 ++++++++++++++ .../coded/spring/ordering/OrdersRepository.kt | 27 ++++++++++++ .../coded/spring/ordering/OrdersService.kt | 29 +++++++++++++ .../coded/spring/ordering/itemsRepository.kt | 20 +++++++++ .../com/coded/spring/users/UsersController.kt | 16 +++++++ .../com/coded/spring/users/UsersRepository.kt | 22 ++++++++++ src/main/resources/application.properties | 6 ++- .../spring/{ordering => }/ApplicationTests.kt | 2 +- 12 files changed, 154 insertions(+), 70 deletions(-) rename src/main/kotlin/com/coded/spring/{ordering => }/Application.kt (87%) delete mode 100644 src/main/kotlin/com/coded/spring/ordering/OnlineOrderRepository.kt delete mode 100644 src/main/kotlin/com/coded/spring/ordering/OnlineOrderingController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/OrdersController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/OrdersService.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/itemsRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/users/UsersController.kt create mode 100644 src/main/kotlin/com/coded/spring/users/UsersRepository.kt rename src/test/kotlin/com/coded/spring/{ordering => }/ApplicationTests.kt (83%) diff --git a/pom.xml b/pom.xml index be47663..2724561 100644 --- a/pom.xml +++ b/pom.xml @@ -62,8 +62,8 @@ spring-boot-starter-data-jpa - com.h2database - h2 + org.postgresql + postgresql org.jetbrains.kotlin diff --git a/src/main/kotlin/com/coded/spring/ordering/Application.kt b/src/main/kotlin/com/coded/spring/Application.kt similarity index 87% rename from src/main/kotlin/com/coded/spring/ordering/Application.kt rename to src/main/kotlin/com/coded/spring/Application.kt index 8554e49..3fe2c8e 100644 --- a/src/main/kotlin/com/coded/spring/ordering/Application.kt +++ b/src/main/kotlin/com/coded/spring/Application.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package com.coded.spring import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication diff --git a/src/main/kotlin/com/coded/spring/ordering/OnlineOrderRepository.kt b/src/main/kotlin/com/coded/spring/ordering/OnlineOrderRepository.kt deleted file mode 100644 index 3cb405c..0000000 --- a/src/main/kotlin/com/coded/spring/ordering/OnlineOrderRepository.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.coded.spring.ordering - -import jakarta.inject.Named -import jakarta.persistence.* -import org.springframework.data.jpa.repository.JpaRepository - -@Named -interface OrderRepository : JpaRepository - -@Entity -@Table(name = "usernames") -data class Order( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - val id: Long? = null, - @Column(name="username") - val username: String, - val resturant: String, - @CollectionTable - val items: MutableList -){ - constructor() : this(null,"", "", mutableListOf()) -} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/OnlineOrderingController.kt b/src/main/kotlin/com/coded/spring/ordering/OnlineOrderingController.kt deleted file mode 100644 index 9ee3476..0000000 --- a/src/main/kotlin/com/coded/spring/ordering/OnlineOrderingController.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.coded.spring.ordering - -import org.springframework.boot.autoconfigure.security.SecurityProperties -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RestController -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.RequestBody - -@RestController -class OnlineOrderingController( - val orderRepository: OrderRepository -){ - - @GetMapping("/helloOrders") - fun hello() = - orderRepository.findAll() - - - @PostMapping("/orders") - // this is the command - fun addOrder( @RequestBody request: OrderRequest){ - val addUser = Order( - username = request.user, - resturant = request.restaurant, - items = request.items - ) - } - @PostMapping("/orders1") - // this is the same command but we can save it (better) - fun saveOrders( @RequestBody request: OrderRequest) = orderRepository.save( - Order(username = request.user, - resturant = request.restaurant, - items = request.items) - ) - -} - -data class OrderRequest( - val user: String , - val restaurant: String , - val items: MutableList -) 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..2ce4a5d --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt @@ -0,0 +1,31 @@ +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 ordersController( + val ordersService: OrdersService, + val ordersRepository: OrdersRepository, + val itemsRepository: ItemsRepository +){ + + @GetMapping("/orders/v1/list") + fun showUsers() = ordersRepository.findAll() + + @PostMapping("/orders") + fun sayOrder( @RequestBody request: OrderRequest) + = ordersService.createOrder(request.userId, request.items) +} + + +data class OrderRequest( + val userId: Long, + val items: List +) +data class Item( + val name: String, + val quantity : Int +) 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..3edf87b --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt @@ -0,0 +1,27 @@ +package com.coded.spring.ordering + +import com.coded.spring.users.UserEntity +import jakarta.inject.Named +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface OrdersRepository : JpaRepository { + fun findByUserId(userId: Long): List +} + +@Entity +@Table(name = "orders") +data class OrdersEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + + @ManyToOne + val user: UserEntity, + + @OneToMany(mappedBy = "orderId") + val items: List? = null +){ + constructor() : this(null, UserEntity(), ) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersService.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersService.kt new file mode 100644 index 0000000..62a3169 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersService.kt @@ -0,0 +1,29 @@ +package com.coded.spring.ordering + +import com.coded.spring.users.UsersRepository +import jakarta.inject.Named + + +@Named +class OrdersService( + val usersRepo : UsersRepository, + val ordersRepo : OrdersRepository, + val itemsRepository: ItemsRepository +) { + fun createOrder(userId: Long, items: List): OrdersEntity { + val user = usersRepo.findById(userId).get() + val newOrder = OrdersEntity(user = user) + val savedOrder = ordersRepo.save(newOrder) + val newItems = items.map{ + ItemEntity( + orderId = savedOrder.id!!, + name = it.name , + quantity = it.quantity + ) + } + itemsRepository.saveAll(newItems) + return savedOrder + + + } +} \ 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 new file mode 100644 index 0000000..b746b7c --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/itemsRepository.kt @@ -0,0 +1,20 @@ +package com.coded.spring.ordering + +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository + +interface ItemsRepository : JpaRepository + +@Entity +@Table(name="items") +data class ItemEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id : Long? = null, + val orderId : Long, + val name : String, + @Column(name = "quentity") + val quantity : Int +){ + constructor() : this(null, 0, "", 0) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/users/UsersController.kt b/src/main/kotlin/com/coded/spring/users/UsersController.kt new file mode 100644 index 0000000..4bb6053 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/users/UsersController.kt @@ -0,0 +1,16 @@ +package com.coded.spring.users + +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RestController + + +@RestController +class UsersController( + val usersRepository: UsersRepository +){ + + @GetMapping("/users/v1/list") + fun sayUsers() = usersRepository.findAll() + +} + diff --git a/src/main/kotlin/com/coded/spring/users/UsersRepository.kt b/src/main/kotlin/com/coded/spring/users/UsersRepository.kt new file mode 100644 index 0000000..293f7a4 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/users/UsersRepository.kt @@ -0,0 +1,22 @@ +package com.coded.spring.users + +import jakarta.inject.Named +import jakarta.persistence.* +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface UsersRepository : JpaRepository { + fun age(age: Int): MutableList +} + +@Entity +@Table(name = "users") +data class UserEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + var name: String, + var age: Int +){ + constructor() : this(null, "", 0) +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 57a5e62..9ca379a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,2 +1,6 @@ spring.application.name=Kotlin.SpringbootV2 -server.port = 8081 \ No newline at end of file +server.port = 8081 +spring.datasource.url=jdbc:postgresql://localhost:5432/OnlineOrdersDB +spring.datasource.username=postgres +spring.datasource.password=7150 +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file diff --git a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt b/src/test/kotlin/com/coded/spring/ApplicationTests.kt similarity index 83% rename from src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt rename to src/test/kotlin/com/coded/spring/ApplicationTests.kt index b2e2320..989976b 100644 --- a/src/test/kotlin/com/coded/spring/ordering/ApplicationTests.kt +++ b/src/test/kotlin/com/coded/spring/ApplicationTests.kt @@ -1,4 +1,4 @@ -package com.coded.spring.ordering +package com.coded.spring import org.junit.jupiter.api.Test import org.springframework.boot.test.context.SpringBootTest From dad59e4025633e3326e9ff24568dced83be9a0c5 Mon Sep 17 00:00:00 2001 From: Mishal Alquraini Date: Thu, 17 Apr 2025 22:00:26 +0300 Subject: [PATCH 03/10] did the validations and online order authentication task --- pom.xml | 4 ++ .../kotlin/com/coded/spring/InitUserRunner.kt | 43 ++++++++++++++ .../authentication/CustomerDetailsService.kt | 26 +++++++++ .../spring/authentication/SecurityConfig.kt | 40 +++++++++++++ .../coded/spring/ordering/MenuController.kt | 24 ++++++++ .../com/coded/spring/ordering/MenuEntity.kt | 18 ++++++ .../coded/spring/ordering/MenuRepository.kt | 7 +++ .../com/coded/spring/ordering/MenuService.kt | 38 +++++++++++++ .../com/coded/spring/users/UserService.kt | 57 +++++++++++++++++++ .../com/coded/spring/users/UsersController.kt | 22 ++++++- .../com/coded/spring/users/UsersRepository.kt | 9 ++- 11 files changed, 284 insertions(+), 4 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/InitUserRunner.kt create mode 100644 src/main/kotlin/com/coded/spring/authentication/CustomerDetailsService.kt create mode 100644 src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/MenuController.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/MenuEntity.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/MenuRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/ordering/MenuService.kt create mode 100644 src/main/kotlin/com/coded/spring/users/UserService.kt diff --git a/pom.xml b/pom.xml index 2724561..de452fc 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,10 @@ org.springframework.boot spring-boot-starter-data-jpa + + org.springframework.boot + spring-boot-starter-security + org.postgresql postgresql diff --git a/src/main/kotlin/com/coded/spring/InitUserRunner.kt b/src/main/kotlin/com/coded/spring/InitUserRunner.kt new file mode 100644 index 0000000..6a418a5 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/InitUserRunner.kt @@ -0,0 +1,43 @@ +package com.coded.spring + +import com.coded.spring.users.UserEntity +import com.coded.spring.users.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 initUser( + usersRepository: UsersRepository, + passwordEncoder: PasswordEncoder + ) = CommandLineRunner { + val user = UserEntity( + name = "Meshal", + age = 24, + username = "meshal77", + password = passwordEncoder.encode("Meshal99775283") + ) + if (usersRepository.findByUsername(user.username) == null) { + println("Creating user ${user.username}") + usersRepository.save(user) + } else { + println("User ${user.username} #${user.id} already exists") + +// } +// try{ +// userRepository.findByUsername(user.username) +// } +// catch (){ +// + } + } +} + +fun main(args: Array) { + runApplication(*args) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/authentication/CustomerDetailsService.kt b/src/main/kotlin/com/coded/spring/authentication/CustomerDetailsService.kt new file mode 100644 index 0000000..62df910 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/authentication/CustomerDetailsService.kt @@ -0,0 +1,26 @@ +package com.coded.spring.authentication + +import com.coded.spring.users.UserEntity +import com.coded.spring.users.UsersRepository +import org.springframework.security.core.userdetails.User +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 +class CustomerDetailsService( + private val userRepo : UsersRepository +): UserDetailsService { + override fun loadUserByUsername(username: String): UserDetails { + val user: UserEntity = userRepo.findByUsername(username) ?: + throw UsernameNotFoundException("User not found ...") + + return User.builder() + .username(user.username) + .password(user.password) + .build() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt new file mode 100644 index 0000000..58ab09a --- /dev/null +++ b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt @@ -0,0 +1,40 @@ +package com.coded.spring.authentication + +import jdk.jfr.Enabled +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +import org.springframework.security.core.userdetails.UserDetails +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 + + +@Configuration +@EnableWebSecurity +class SecurityConf ( + private val userDetailsService: UserDetailsService +){ + + @Bean + fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder() + + @Bean + fun securityFilterChain(http: HttpSecurity): SecurityFilterChain{ + http.csrf{ it.disable()}.authorizeHttpRequests { +// it.requestMatchers("/users/v1/list").permitAll() +// .anyRequest().authenticated() +// it.requestMatchers("/users/v1/create/menu").permitAll() +// .anyRequest().permitAll() + it.requestMatchers("/Public/**").permitAll().requestMatchers("/users/v1/**") + .authenticated() + + } + .formLogin { + it.defaultSuccessUrl("/welcome", true)} + .userDetailsService(userDetailsService) + return http.build() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuController.kt b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt new file mode 100644 index 0000000..eac1de9 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt @@ -0,0 +1,24 @@ +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 MenuController( + val menuRepository: MenuRepository, + val menuService: MenuService +){ + + @GetMapping("/public/users/v1/list/menu") + fun sayUsers() = menuRepository.findAll() + + + @PostMapping("/users/v1/create/menu") + fun menuAdd(@RequestBody request: RequestMenu): MenuEntity { + val newMenu = menuService.addMenu(request) + return newMenu + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuEntity.kt b/src/main/kotlin/com/coded/spring/ordering/MenuEntity.kt new file mode 100644 index 0000000..ee0b055 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/MenuEntity.kt @@ -0,0 +1,18 @@ +package com.coded.spring.ordering + +import jakarta.persistence.* +import java.math.BigDecimal + +@Entity +@Table(name="menu") +data class MenuEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, +// @Column(name = "name") +// val myFieldName: String, + val name: String, + val price: BigDecimal +){ + constructor() : this(null,"", BigDecimal.ZERO) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuRepository.kt b/src/main/kotlin/com/coded/spring/ordering/MenuRepository.kt new file mode 100644 index 0000000..d07cacd --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/MenuRepository.kt @@ -0,0 +1,7 @@ +package com.coded.spring.ordering + +import jakarta.inject.Named +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface MenuRepository : JpaRepository \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuService.kt b/src/main/kotlin/com/coded/spring/ordering/MenuService.kt new file mode 100644 index 0000000..9d56315 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/ordering/MenuService.kt @@ -0,0 +1,38 @@ +package com.coded.spring.ordering + +import org.springframework.stereotype.Service +import java.math.BigDecimal + +@Service +class MenuService( + val menuRepository: MenuRepository +){ + fun addMenu(request: RequestMenu) : MenuEntity{ + val newMenu = MenuEntity( + name = request.menuName, + price = request.price + ) + return menuRepository.save(newMenu) + } + + fun listMenu(): List = menuRepository.findAll().map { + Menu( + menuName = it.name, + price = it.price + ) + } + +} + + + + +data class Menu( + val menuName: String, + val price: BigDecimal +) + +data class RequestMenu( + val menuName: String, + val price: BigDecimal +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/users/UserService.kt b/src/main/kotlin/com/coded/spring/users/UserService.kt new file mode 100644 index 0000000..edaefaf --- /dev/null +++ b/src/main/kotlin/com/coded/spring/users/UserService.kt @@ -0,0 +1,57 @@ +package com.coded.spring.users + +import jakarta.inject.Named +import org.springframework.security.crypto.password.PasswordEncoder + +@Named +class UsersService( + private val usersRepository: UsersRepository, + private val encoder: PasswordEncoder, +) { + + fun createUser(request: AddUserRequest): addedUserResponse{ + val myNewUserEntity = UserEntity( + name = request.name, + age = request.age, + username = request.username, + password = encoder.encode(request.password), + ) + + val savedUser= usersRepository.save(myNewUserEntity) + + return addedUserResponse(savedUser.id,savedUser.username) + } + + fun listUsers(): List = usersRepository.findAll().map { + User( + name = it.name, + age = it.age + ) + } + +} + +fun isValidPassword(password: String): Boolean { + + return password.length >= 6 && password.any { it.isDigit() } && password.any { it.isUpperCase() } +} + + +data class AddUserRequest( + val name: String, + val username: String, + val password: String, + val age: Int +) + + + +data class addedUserResponse( + val id: Long?, + val username: String +) + +data class User( + val name: String, + val age: Int +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/users/UsersController.kt b/src/main/kotlin/com/coded/spring/users/UsersController.kt index 4bb6053..8b1c4f4 100644 --- a/src/main/kotlin/com/coded/spring/users/UsersController.kt +++ b/src/main/kotlin/com/coded/spring/users/UsersController.kt @@ -1,16 +1,34 @@ package com.coded.spring.users 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 UsersController( - val usersRepository: UsersRepository + val usersRepository: UsersRepository, + val usersService: UsersService ){ + @GetMapping("/welcome") + fun welcome() = "Get in yoo" + + @GetMapping("/users/v1/list") fun sayUsers() = usersRepository.findAll() + + @PostMapping("/users/v1/create") + fun createUser(@RequestBody request: AddUserRequest):Any{ + if (isValidPassword(request.password)){ + val newUser = usersService.createUser(request) + return newUser + } + return "your password is invalid" + + + } + } diff --git a/src/main/kotlin/com/coded/spring/users/UsersRepository.kt b/src/main/kotlin/com/coded/spring/users/UsersRepository.kt index 293f7a4..12c0120 100644 --- a/src/main/kotlin/com/coded/spring/users/UsersRepository.kt +++ b/src/main/kotlin/com/coded/spring/users/UsersRepository.kt @@ -7,6 +7,8 @@ import org.springframework.data.jpa.repository.JpaRepository @Named interface UsersRepository : JpaRepository { fun age(age: Int): MutableList + + fun findByUsername(username: String): UserEntity? } @Entity @@ -16,7 +18,10 @@ data class UserEntity( @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null, var name: String, - var age: Int + var age: Int, + + val username: String, + val password: String ){ - constructor() : this(null, "", 0) + constructor() : this(null, "", 0,"","") } From 3a3bbce5c153bd3b4469553f7eedf09e6f134d81 Mon Sep 17 00:00:00 2001 From: Mishal Alquraini Date: Sun, 20 Apr 2025 21:03:15 +0300 Subject: [PATCH 04/10] done with JWT authentication and profiles --- pom.xml | 17 +++++++ .../kotlin/com/coded/spring/InitUserRunner.kt | 6 +-- .../spring/Profiles/ProfileController.kt | 17 +++++++ .../coded/spring/Profiles/ProfileEntity.kt | 18 ++++++++ .../spring/Profiles/ProfilesRepository.kt | 8 ++++ .../coded/spring/Profiles/ProfilesService.kt | 39 ++++++++++++++++ .../JWT/AuthenticationController.kt | 39 ++++++++++++++++ .../JWT/JwtAuthenticationFilter.kt | 45 ++++++++++++++++++ .../spring/authentication/JWT/JwtService.kt | 43 +++++++++++++++++ .../spring/authentication/SecurityConfig.kt | 46 +++++++++++++++---- .../coded/spring/ordering/MenuController.kt | 2 +- .../com/coded/spring/users/UserService.kt | 3 ++ .../com/coded/spring/users/UsersController.kt | 10 ++-- 13 files changed, 277 insertions(+), 16 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt create mode 100644 src/main/kotlin/com/coded/spring/Profiles/ProfileEntity.kt create mode 100644 src/main/kotlin/com/coded/spring/Profiles/ProfilesRepository.kt create mode 100644 src/main/kotlin/com/coded/spring/Profiles/ProfilesService.kt create mode 100644 src/main/kotlin/com/coded/spring/authentication/JWT/AuthenticationController.kt create mode 100644 src/main/kotlin/com/coded/spring/authentication/JWT/JwtAuthenticationFilter.kt create mode 100644 src/main/kotlin/com/coded/spring/authentication/JWT/JwtService.kt diff --git a/pom.xml b/pom.xml index de452fc..8b3e12f 100644 --- a/pom.xml +++ b/pom.xml @@ -65,6 +65,23 @@ org.springframework.boot spring-boot-starter-security + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + runtime + 0.11.5 + + + io.jsonwebtoken + jjwt-jackson + runtime + 0.11.5 + org.postgresql postgresql diff --git a/src/main/kotlin/com/coded/spring/InitUserRunner.kt b/src/main/kotlin/com/coded/spring/InitUserRunner.kt index 6a418a5..51ee9cd 100644 --- a/src/main/kotlin/com/coded/spring/InitUserRunner.kt +++ b/src/main/kotlin/com/coded/spring/InitUserRunner.kt @@ -19,7 +19,7 @@ class InitUserRunner { val user = UserEntity( name = "Meshal", age = 24, - username = "meshal77", + username = "meshal7", password = passwordEncoder.encode("Meshal99775283") ) if (usersRepository.findByUsername(user.username) == null) { @@ -33,11 +33,11 @@ class InitUserRunner { // userRepository.findByUsername(user.username) // } // catch (){ -// + } } } fun main(args: Array) { - runApplication(*args) + runApplication(*args).close() } \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt b/src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt new file mode 100644 index 0000000..60f8948 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt @@ -0,0 +1,17 @@ +package com.coded.spring.Profiles + +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + +@RestController +class ProfileController( + val profilesRepository: ProfilesRepository, + val profilesService: ProfilesService +){ + @PostMapping("/users/v1/profiles") + fun createProfiles(@RequestBody request: ProfileRequest): ProfileResponse{ + return profilesService.createProfile(request) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/Profiles/ProfileEntity.kt b/src/main/kotlin/com/coded/spring/Profiles/ProfileEntity.kt new file mode 100644 index 0000000..fd6240c --- /dev/null +++ b/src/main/kotlin/com/coded/spring/Profiles/ProfileEntity.kt @@ -0,0 +1,18 @@ +package com.coded.spring.Profiles + +import jakarta.persistence.* + + +@Entity +@Table(name="profiles") +data class ProfileEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + val userID: Long, + val firstName: String, + val lastName: String, + val phoneNumber: Long +){ + constructor() : this(null,0, "","",0) +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/Profiles/ProfilesRepository.kt b/src/main/kotlin/com/coded/spring/Profiles/ProfilesRepository.kt new file mode 100644 index 0000000..f065933 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/Profiles/ProfilesRepository.kt @@ -0,0 +1,8 @@ +package com.coded.spring.Profiles + +import com.coded.spring.ordering.MenuEntity +import jakarta.inject.Named +import org.springframework.data.jpa.repository.JpaRepository + +@Named +interface ProfilesRepository : JpaRepository \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/Profiles/ProfilesService.kt b/src/main/kotlin/com/coded/spring/Profiles/ProfilesService.kt new file mode 100644 index 0000000..9d28883 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/Profiles/ProfilesService.kt @@ -0,0 +1,39 @@ +package com.coded.spring.Profiles + +import com.coded.spring.users.UsersRepository +import jakarta.inject.Named +import org.springframework.security.core.context.SecurityContext +import org.springframework.security.core.context.SecurityContextHolder + +@Named +class ProfilesService ( + private val profilesRepository: ProfilesRepository, + private val usersRepository: UsersRepository +){ + fun createProfile(request: ProfileRequest): ProfileResponse{ + val userId = usersRepository.findByUsername(SecurityContextHolder.getContext().authentication.name)?.id ?: + throw IllegalArgumentException() + val newProfile = ProfileEntity( + userID = userId, + firstName = request.firstName, + lastName = request.lastName, + phoneNumber = request.phoneNumber + ) + val savedProfile = profilesRepository.save(newProfile) + return ProfileResponse(savedProfile.firstName,savedProfile.lastName,savedProfile.phoneNumber) + } +} + +data class ProfileRequest( + val firstName: String, + val lastName: String, + val phoneNumber: Long +) + +data class ProfileResponse( + val firstName: String, + val lastName: String, + val phoneNumber: Long + +) + diff --git a/src/main/kotlin/com/coded/spring/authentication/JWT/AuthenticationController.kt b/src/main/kotlin/com/coded/spring/authentication/JWT/AuthenticationController.kt new file mode 100644 index 0000000..276ced1 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/authentication/JWT/AuthenticationController.kt @@ -0,0 +1,39 @@ +package com.coded.spring.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 +) { + + @PostMapping("/login") + fun login(@RequestBody authRequest: AuthenticationRequest): AuthenticationResponse { + val authToken = UsernamePasswordAuthenticationToken(authRequest.username, authRequest.password) + val authentication = authenticationManager.authenticate(authToken) + + if (authentication.isAuthenticated) { + val userDetails = userDetailsService.loadUserByUsername(authRequest.username) + val token = jwtService.generateToken(userDetails.username) + return AuthenticationResponse (token) + } else { + 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/authentication/JWT/JwtAuthenticationFilter.kt b/src/main/kotlin/com/coded/spring/authentication/JWT/JwtAuthenticationFilter.kt new file mode 100644 index 0000000..c6ccdae --- /dev/null +++ b/src/main/kotlin/com/coded/spring/authentication/JWT/JwtAuthenticationFilter.kt @@ -0,0 +1,45 @@ +package com.coded.spring.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/authentication/JWT/JwtService.kt b/src/main/kotlin/com/coded/spring/authentication/JWT/JwtService.kt new file mode 100644 index 0000000..65e5346 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/authentication/JWT/JwtService.kt @@ -0,0 +1,43 @@ +package com.coded.spring.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/authentication/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt index 58ab09a..c15bf39 100644 --- a/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt +++ b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt @@ -1,22 +1,31 @@ package com.coded.spring.authentication +import com.coded.spring.authentication.JWT.JwtAuthenticationFilter import jdk.jfr.Enabled 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.UserDetails 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 SecurityConf ( - private val userDetailsService: UserDetailsService -){ + private val userDetailsService: UserDetailsService, + private val jwtAuthFilter: JwtAuthenticationFilter, + + ){ @Bean fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder() @@ -28,13 +37,34 @@ class SecurityConf ( // .anyRequest().authenticated() // it.requestMatchers("/users/v1/create/menu").permitAll() // .anyRequest().permitAll() - it.requestMatchers("/Public/**").permitAll().requestMatchers("/users/v1/**") - .authenticated() - +// it.requestMatchers("/Public/**").permitAll().requestMatchers("/users/v1/**") +// .authenticated() +// } +// .formLogin { +// it.defaultSuccessUrl("/Public/menu", true)} +// .userDetailsService(userDetailsService) +// return http.build() + it.requestMatchers("/auth/**", "/users/v1/**").permitAll() + .anyRequest().authenticated() } - .formLogin { - it.defaultSuccessUrl("/welcome", true)} - .userDetailsService(userDetailsService) + .sessionManagement { + it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + } + .authenticationProvider(authenticationProvider()) + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter::class.java) + return http.build() } + + @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/MenuController.kt b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt index eac1de9..205a8d5 100644 --- a/src/main/kotlin/com/coded/spring/ordering/MenuController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt @@ -11,7 +11,7 @@ class MenuController( val menuService: MenuService ){ - @GetMapping("/public/users/v1/list/menu") + @GetMapping("/Public/menu") fun sayUsers() = menuRepository.findAll() diff --git a/src/main/kotlin/com/coded/spring/users/UserService.kt b/src/main/kotlin/com/coded/spring/users/UserService.kt index edaefaf..86cb106 100644 --- a/src/main/kotlin/com/coded/spring/users/UserService.kt +++ b/src/main/kotlin/com/coded/spring/users/UserService.kt @@ -16,6 +16,8 @@ class UsersService( username = request.username, password = encoder.encode(request.password), ) + if (request.username.isBlank()){ + throw IllegalArgumentException("username not correct")} val savedUser= usersRepository.save(myNewUserEntity) @@ -31,6 +33,7 @@ class UsersService( } + fun isValidPassword(password: String): Boolean { return password.length >= 6 && password.any { it.isDigit() } && password.any { it.isUpperCase() } diff --git a/src/main/kotlin/com/coded/spring/users/UsersController.kt b/src/main/kotlin/com/coded/spring/users/UsersController.kt index 8b1c4f4..a8b4467 100644 --- a/src/main/kotlin/com/coded/spring/users/UsersController.kt +++ b/src/main/kotlin/com/coded/spring/users/UsersController.kt @@ -1,5 +1,6 @@ package com.coded.spring.users +import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody @@ -21,11 +22,12 @@ class UsersController( @PostMapping("/users/v1/create") fun createUser(@RequestBody request: AddUserRequest):Any{ - if (isValidPassword(request.password)){ - val newUser = usersService.createUser(request) - return newUser + return try { + usersService.createUser(request) + ResponseEntity.ok("user is good to go") + } catch (e: IllegalArgumentException){ + ResponseEntity.badRequest().body(e.message) } - return "your password is invalid" } From 3de1c62acab7aab9d2806ba4d55c3d1c20a88dd2 Mon Sep 17 00:00:00 2001 From: Mishal Alquraini Date: Mon, 21 Apr 2025 20:05:37 +0300 Subject: [PATCH 05/10] did 1 get test and 1 post test --- pom.xml | 23 +++++++++-- .../spring/Profiles/ProfileController.kt | 5 ++- .../{ => authentication}/InitUserRunner.kt | 3 +- .../spring/authentication/SecurityConfig.kt | 1 - .../com/coded/spring/ApplicationTests.kt | 13 ------ .../com/coded/spring/test/ApplicationTests.kt | 40 +++++++++++++++++++ 6 files changed, 65 insertions(+), 20 deletions(-) rename src/main/kotlin/com/coded/spring/{ => authentication}/InitUserRunner.kt (94%) delete mode 100644 src/test/kotlin/com/coded/spring/ApplicationTests.kt create mode 100644 src/test/kotlin/com/coded/spring/test/ApplicationTests.kt diff --git a/pom.xml b/pom.xml index 8b3e12f..806a5e1 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,20 @@ org.jetbrains.kotlin kotlin-reflect + + org.postgresql + postgresql + compile + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.h2database + h2 + test + org.jetbrains.kotlin kotlin-stdlib @@ -53,6 +67,11 @@ spring-boot-starter-test test + + org.jetbrains.kotlin + kotlin-test-junit5 + test + jakarta.inject jakarta.inject-api @@ -82,10 +101,6 @@ runtime 0.11.5 - - org.postgresql - postgresql - org.jetbrains.kotlin kotlin-test-junit5 diff --git a/src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt b/src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt index 60f8948..ff0819d 100644 --- a/src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt +++ b/src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt @@ -1,5 +1,6 @@ package com.coded.spring.Profiles +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 @@ -9,9 +10,11 @@ class ProfileController( val profilesRepository: ProfilesRepository, val profilesService: ProfilesService ){ - @PostMapping("/users/v1/profiles") + @PostMapping("/auth/users/v1/profiles") fun createProfiles(@RequestBody request: ProfileRequest): ProfileResponse{ return profilesService.createProfile(request) } + @GetMapping("/auth/users/v1/profiles/list") + fun showProfiles() = profilesRepository.findAll() } \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/InitUserRunner.kt b/src/main/kotlin/com/coded/spring/authentication/InitUserRunner.kt similarity index 94% rename from src/main/kotlin/com/coded/spring/InitUserRunner.kt rename to src/main/kotlin/com/coded/spring/authentication/InitUserRunner.kt index 51ee9cd..dc321ca 100644 --- a/src/main/kotlin/com/coded/spring/InitUserRunner.kt +++ b/src/main/kotlin/com/coded/spring/authentication/InitUserRunner.kt @@ -1,5 +1,6 @@ -package com.coded.spring +package com.coded.spring.authentication +import com.coded.spring.Application import com.coded.spring.users.UserEntity import com.coded.spring.users.UsersRepository import org.springframework.boot.CommandLineRunner diff --git a/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt index c15bf39..c6e93bf 100644 --- a/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt +++ b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt @@ -20,7 +20,6 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic @Configuration -@EnableWebSecurity class SecurityConf ( private val userDetailsService: UserDetailsService, private val jwtAuthFilter: JwtAuthenticationFilter, diff --git a/src/test/kotlin/com/coded/spring/ApplicationTests.kt b/src/test/kotlin/com/coded/spring/ApplicationTests.kt deleted file mode 100644 index 989976b..0000000 --- a/src/test/kotlin/com/coded/spring/ApplicationTests.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.coded.spring - -import org.junit.jupiter.api.Test -import org.springframework.boot.test.context.SpringBootTest - -@SpringBootTest -class ApplicationTests { - - @Test - fun contextLoads() { - } - -} diff --git a/src/test/kotlin/com/coded/spring/test/ApplicationTests.kt b/src/test/kotlin/com/coded/spring/test/ApplicationTests.kt new file mode 100644 index 0000000..42b2795 --- /dev/null +++ b/src/test/kotlin/com/coded/spring/test/ApplicationTests.kt @@ -0,0 +1,40 @@ +package com.coded.spring.test + +import com.coded.spring.ordering.OrderRequest +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.http.HttpStatus +import javax.swing.Spring +import kotlin.test.assertEquals + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class ApplicationTests { + + @Autowired + lateinit var restTemplate: TestRestTemplate + + @Test + fun testOrderList() { + val result = restTemplate.getForEntity( + "/welcome", + String::class.java + ) + val expected = "Get in yoo" + + assertEquals(HttpStatus.OK, result.statusCode) + assertEquals(expected, result.body) + } + + @Test + fun testCreateOrder(){ + val result = restTemplate.postForEntity( + "/orders", //Endpoint + OrderRequest(1, listOf()), //Request body + String::class.java // Response body + ) + assertEquals(HttpStatus.OK, result.statusCode) + } + +} From bab4b5f96f8a68b0e9c5c1d38c401aef76242174 Mon Sep 17 00:00:00 2001 From: Mishal Alquraini Date: Wed, 23 Apr 2025 19:09:00 +0300 Subject: [PATCH 06/10] deleted interfering InitUserRunner.kt --- .../spring/authentication/InitUserRunner.kt | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 src/main/kotlin/com/coded/spring/authentication/InitUserRunner.kt diff --git a/src/main/kotlin/com/coded/spring/authentication/InitUserRunner.kt b/src/main/kotlin/com/coded/spring/authentication/InitUserRunner.kt deleted file mode 100644 index dc321ca..0000000 --- a/src/main/kotlin/com/coded/spring/authentication/InitUserRunner.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.coded.spring.authentication - -import com.coded.spring.Application -import com.coded.spring.users.UserEntity -import com.coded.spring.users.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 initUser( - usersRepository: UsersRepository, - passwordEncoder: PasswordEncoder - ) = CommandLineRunner { - val user = UserEntity( - name = "Meshal", - age = 24, - username = "meshal7", - password = passwordEncoder.encode("Meshal99775283") - ) - if (usersRepository.findByUsername(user.username) == null) { - println("Creating user ${user.username}") - usersRepository.save(user) - } else { - println("User ${user.username} #${user.id} already exists") - -// } -// try{ -// userRepository.findByUsername(user.username) -// } -// catch (){ - - } - } -} - -fun main(args: Array) { - runApplication(*args).close() -} \ No newline at end of file From 5b30af50c4f72ea03637d6d3f5be6ca910c9e864 Mon Sep 17 00:00:00 2001 From: Mishal Alquraini Date: Tue, 29 Apr 2025 13:35:01 +0300 Subject: [PATCH 07/10] did the config. and discount. --- pom.xml | 21 +++-- .../kotlin/com/coded/spring/Loggingfilter.kt | 34 ++++++++ .../spring/authentication/JWT/JwtService.kt | 10 ++- .../spring/authentication/SecurityConfig.kt | 3 +- .../coded/spring/ordering/MenuController.kt | 7 +- .../coded/spring/ordering/OrdersController.kt | 21 ++++- .../coded/spring/ordering/OrdersService.kt | 52 +++++++++-- .../coded/spring/ordering/itemsRepository.kt | 4 +- .../com/coded/spring/pets/PetsClient.kt | 32 +++++++ .../com/coded/spring/pets/PetsController.kt | 47 ++++++++++ .../com/coded/spring/pets/PetsService.kt | 24 +++++ .../com/coded/spring/users/UsersController.kt | 7 +- src/main/resources/application.properties | 3 +- .../com/coded/spring/test/ApplicationTests.kt | 87 ++++++++++++++++--- 14 files changed, 314 insertions(+), 38 deletions(-) create mode 100644 src/main/kotlin/com/coded/spring/Loggingfilter.kt create mode 100644 src/main/kotlin/com/coded/spring/pets/PetsClient.kt create mode 100644 src/main/kotlin/com/coded/spring/pets/PetsController.kt create mode 100644 src/main/kotlin/com/coded/spring/pets/PetsService.kt diff --git a/pom.xml b/pom.xml index 806a5e1..ee50dba 100644 --- a/pom.xml +++ b/pom.xml @@ -46,17 +46,26 @@ org.postgresql postgresql - compile + runtime - org.springframework.boot - spring-boot-starter-data-jpa + com.hazelcast + hazelcast + 5.3.8 - com.h2database - h2 - test + org.springdoc + springdoc-openapi-starter-webmvc-api + 2.6.0 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.jetbrains.kotlin kotlin-stdlib diff --git a/src/main/kotlin/com/coded/spring/Loggingfilter.kt b/src/main/kotlin/com/coded/spring/Loggingfilter.kt new file mode 100644 index 0000000..c9c5f41 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/Loggingfilter.kt @@ -0,0 +1,34 @@ +package com.coded.spring + +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.core.annotation.Order +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter +import org.springframework.web.util.ContentCachingRequestWrapper +import org.springframework.web.util.ContentCachingResponseWrapper + +@Component +@Order(1) // to select this filter order in referral to other filters +class LoggingFilter : OncePerRequestFilter() { + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + val cachedRequest = ContentCachingRequestWrapper(request) + val cashedResponse = ContentCachingResponseWrapper(response) + filterChain.doFilter(cachedRequest,cashedResponse) + cashedResponse.copyBodyToResponse() + + val requestBody = String(cachedRequest.contentAsByteArray) + logger.info("Request: method=${request.method}, url=${request.requestURI}, body ${requestBody}") + + val responseBody = String(cashedResponse.contentAsByteArray) + logger.info("Request: status=${response.status}, body=$responseBody") + + cashedResponse.copyBodyToResponse() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/authentication/JWT/JwtService.kt b/src/main/kotlin/com/coded/spring/authentication/JWT/JwtService.kt index 65e5346..69f76fb 100644 --- a/src/main/kotlin/com/coded/spring/authentication/JWT/JwtService.kt +++ b/src/main/kotlin/com/coded/spring/authentication/JWT/JwtService.kt @@ -3,14 +3,18 @@ package com.coded.spring.authentication.JWT import io.jsonwebtoken.* import io.jsonwebtoken.security.Keys +import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component import java.util.* import javax.crypto.SecretKey @Component -class JwtService { - - private val secretKey: SecretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256) +class JwtService ( + @Value("\${jwt-secret}") + private val secretKeyString: String +){ + private val secretKey: SecretKey = Keys.hmacShaKeyFor(secretKeyString.encodeToByteArray()) +// private val secretKey: SecretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256) private val expirationMs: Long = 1000 * 60 * 60 fun generateToken(username: String): String { diff --git a/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt index c6e93bf..9d10c32 100644 --- a/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt +++ b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt @@ -20,6 +20,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic @Configuration +@EnableWebSecurity class SecurityConf ( private val userDetailsService: UserDetailsService, private val jwtAuthFilter: JwtAuthenticationFilter, @@ -43,7 +44,7 @@ class SecurityConf ( // it.defaultSuccessUrl("/Public/menu", true)} // .userDetailsService(userDetailsService) // return http.build() - it.requestMatchers("/auth/**", "/users/v1/**").permitAll() + it.requestMatchers("/auth/**", "/users/v1/**","/api-docs","/welcome").permitAll() .anyRequest().authenticated() } .sessionManagement { diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuController.kt b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt index 205a8d5..954add0 100644 --- a/src/main/kotlin/com/coded/spring/ordering/MenuController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt @@ -1,5 +1,6 @@ package com.coded.spring.ordering +import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody @@ -16,9 +17,9 @@ class MenuController( @PostMapping("/users/v1/create/menu") - fun menuAdd(@RequestBody request: RequestMenu): MenuEntity { - val newMenu = menuService.addMenu(request) - return newMenu + fun menuAdd(@RequestBody requestMenu: RequestMenu): Any { +// val newMenu = menuService.addMenu(request) + return ResponseEntity.ok().body(menuService.addMenu(requestMenu)) } } \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt index 2ce4a5d..20b9793 100644 --- a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt @@ -4,6 +4,7 @@ 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 java.math.BigDecimal @RestController class ordersController( @@ -16,8 +17,16 @@ class ordersController( fun showUsers() = ordersRepository.findAll() @PostMapping("/orders") - fun sayOrder( @RequestBody request: OrderRequest) - = ordersService.createOrder(request.userId, request.items) + fun sayOrder( @RequestBody request: OrderRequest) : OrderResponse { + val result = ordersService.createOrder(request.userId, request.items) + // return OrderResponse(result.userId, result.items) both ways r correct + return result.let{ + OrderResponse( + userId = it.userId, + items = it.items + ) + } + } } @@ -25,7 +34,13 @@ data class OrderRequest( val userId: Long, val items: List ) +data class OrderResponse( + val userId: Long, + val items: List +) + data class Item( val name: String, - val quantity : Int + val quantity : Int, + val price: BigDecimal ) diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersService.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersService.kt index 62a3169..b8d2d25 100644 --- a/src/main/kotlin/com/coded/spring/ordering/OrdersService.kt +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersService.kt @@ -2,28 +2,68 @@ package com.coded.spring.ordering import com.coded.spring.users.UsersRepository import jakarta.inject.Named +import org.springframework.beans.factory.annotation.Value +import java.math.BigDecimal @Named class OrdersService( val usersRepo : UsersRepository, val ordersRepo : OrdersRepository, - val itemsRepository: ItemsRepository + val itemsRepository: ItemsRepository, + @Value("\${discount.feature}") + val discount: Boolean ) { - fun createOrder(userId: Long, items: List): OrdersEntity { + fun createOrder(userId: Long, items: List): Order { val user = usersRepo.findById(userId).get() val newOrder = OrdersEntity(user = user) val savedOrder = ordersRepo.save(newOrder) + val newItems = items.map{ ItemEntity( orderId = savedOrder.id!!, name = it.name , + price = if (discount)it.price.multiply(BigDecimal(0.8)) else it.price, quantity = it.quantity ) } - itemsRepository.saveAll(newItems) - return savedOrder - + val savedItems = itemsRepository.saveAll(newItems) + return Order( + userId = savedOrder.id!!, + items = savedItems.map { + Item( + name = it.name, + price = it.price, + quantity = it.quantity + ) + } + ) } -} \ No newline at end of file +// if (discount){ +// itemsRepository.saveAll(items.map { +// ItemEntity( +// orderId = savedOrder.id!!, +// name = it.name, +// price = it.price.multiply(BigDecimal(0.8)), +// quantity = it.quantity +// +// ) +// }) +// }else{ +// itemsRepository.saveAll(items.map { +// ItemEntity( +// orderId = savedOrder.id!!, +// name = it.name, +// price = it.price, +// quantity = it.quantity +// +// ) +// }) +// +// } +} +data class Order( + val userId: Long, + val items: List +) \ 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 index b746b7c..ee5d6fc 100644 --- a/src/main/kotlin/com/coded/spring/ordering/itemsRepository.kt +++ b/src/main/kotlin/com/coded/spring/ordering/itemsRepository.kt @@ -2,6 +2,7 @@ package com.coded.spring.ordering import jakarta.persistence.* import org.springframework.data.jpa.repository.JpaRepository +import java.math.BigDecimal interface ItemsRepository : JpaRepository @@ -13,8 +14,9 @@ data class ItemEntity( val id : Long? = null, val orderId : Long, val name : String, + val price : BigDecimal, @Column(name = "quentity") val quantity : Int ){ - constructor() : this(null, 0, "", 0) + constructor() : this(null, 0, "", BigDecimal.ZERO,0) } \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/pets/PetsClient.kt b/src/main/kotlin/com/coded/spring/pets/PetsClient.kt new file mode 100644 index 0000000..bd8d433 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/pets/PetsClient.kt @@ -0,0 +1,32 @@ +package com.coded.spring.pets + +import jakarta.inject.Named +import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.HttpMethod +import org.springframework.web.client.RestTemplate +import org.springframework.web.client.exchange + +@Named +class PetsClient { + fun getPets(): List{ + val restTemplate = RestTemplate() + val url = "https://pets-react-query-backend.eapi.joincoded.com/pets" + + val response = restTemplate.exchange>( + url = url, + method = HttpMethod.GET, + requestEntity = null, + object : ParameterizedTypeReference?>() { + } + ) + return response.body ?: listOf() + } +} + +data class PetDTO( + val id: Long, + val name: String, + val type: String, + val adopted: String, + val image: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/pets/PetsController.kt b/src/main/kotlin/com/coded/spring/pets/PetsController.kt new file mode 100644 index 0000000..fbff989 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/pets/PetsController.kt @@ -0,0 +1,47 @@ +package com.coded.spring.pets + +import io.swagger.v3.oas.annotations.media.Content +import io.swagger.v3.oas.annotations.responses.ApiResponse +import io.swagger.v3.oas.annotations.responses.ApiResponses +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RestController + +@Tag(name="PetsAPI") +@RestController +class PetsController( + private val petsService: PetsService +) { + @ApiResponses( + ApiResponse(responseCode = "200", description = "List of pets available in the store", + content = [Content(mediaType = "application/json")]), + ApiResponse(responseCode = "400", description = "An error occured while listing pets...", + content = [Content(mediaType = "application/json")]) + ) + + @GetMapping("/pets/v1/pets") + fun listPets() : ResponseEntity<*> { + return try{ + ResponseEntity.ok( + ListPetsSuccessfulResponse( + pets = petsService.ListPets() + ) + ) + } catch (e: IllegalStateException){ + ResponseEntity.badRequest().body( + ListPetsFailureResponse( + error = "Sorry, pets are sleeping" + ) + ) + } + } +} + +data class ListPetsSuccessfulResponse( + val pets: List +) + +data class ListPetsFailureResponse( + val error: String +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/pets/PetsService.kt b/src/main/kotlin/com/coded/spring/pets/PetsService.kt new file mode 100644 index 0000000..32a1f11 --- /dev/null +++ b/src/main/kotlin/com/coded/spring/pets/PetsService.kt @@ -0,0 +1,24 @@ +package com.coded.spring.pets + +import jakarta.inject.Named + + +@Named +class PetsService( + private val petsClient: PetsClient +){ + fun ListPets() : List = petsClient.getPets().map { + Pet( + name = it.name, + type = it.type + ) + } +} + + + +data class Pet( + val name: String, + val type: String +) +//val petsCache = serverCache \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/users/UsersController.kt b/src/main/kotlin/com/coded/spring/users/UsersController.kt index a8b4467..5de4230 100644 --- a/src/main/kotlin/com/coded/spring/users/UsersController.kt +++ b/src/main/kotlin/com/coded/spring/users/UsersController.kt @@ -1,5 +1,6 @@ package com.coded.spring.users +import org.springframework.beans.factory.annotation.Value import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping @@ -9,11 +10,13 @@ import org.springframework.web.bind.annotation.RestController @RestController class UsersController( val usersRepository: UsersRepository, - val usersService: UsersService + val usersService: UsersService, + @Value("\${server-welcome-message}") + val companyMessage: String, ){ @GetMapping("/welcome") - fun welcome() = "Get in yoo" + fun welcome() = "Welcome to Online Ordering by $companyMessage" @GetMapping("/users/v1/list") diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9ca379a..b4acdf3 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,4 +3,5 @@ server.port = 8081 spring.datasource.url=jdbc:postgresql://localhost:5432/OnlineOrdersDB spring.datasource.username=postgres spring.datasource.password=7150 -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +springdoc.api-docs.path=/api-docs \ No newline at end of file diff --git a/src/test/kotlin/com/coded/spring/test/ApplicationTests.kt b/src/test/kotlin/com/coded/spring/test/ApplicationTests.kt index 42b2795..a7e7acb 100644 --- a/src/test/kotlin/com/coded/spring/test/ApplicationTests.kt +++ b/src/test/kotlin/com/coded/spring/test/ApplicationTests.kt @@ -1,40 +1,103 @@ package com.coded.spring.test +import com.coded.spring.authentication.JWT.JwtService +import com.coded.spring.ordering.Item import com.coded.spring.ordering.OrderRequest +import com.coded.spring.ordering.OrderResponse +import com.coded.spring.users.UserEntity +import com.coded.spring.users.UsersRepository +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.http.HttpEntity +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import javax.swing.Spring +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.test.context.ActiveProfiles +import org.springframework.util.MultiValueMap import kotlin.test.assertEquals +// how to build test (not really working need to match the tests with the endpoints) + @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class ApplicationTests { - @Autowired lateinit var restTemplate: TestRestTemplate +// the correct way with jwt + companion object { + @JvmStatic + @BeforeAll + fun setUp( + @Autowired usersRepository: UsersRepository, + @Autowired passwordEncoder: PasswordEncoder, + ){ + usersRepository.deleteAll() + val testUser = UserEntity( + name = "coded", + age = 24, + username = "meshal", + password = passwordEncoder.encode("meshal99775283") + ) + val savedUser = usersRepository.save(testUser) + print("savedUser ${savedUser.id}") + } + } @Test - fun testOrderList() { - val result = restTemplate.getForEntity( + fun testWelcome(@Autowired jwtService: JwtService) { + val token = jwtService.generateToken("meshal") + val headers = HttpHeaders( + MultiValueMap.fromSingleValue(mapOf("Authorization" to "Bearer $token")) + ) + val requestEntity = HttpEntity(headers) + + val result = restTemplate.exchange( "/welcome", + HttpMethod.GET, + requestEntity, String::class.java ) - val expected = "Get in yoo" - assertEquals(HttpStatus.OK, result.statusCode) - assertEquals(expected, result.body) + assertEquals("Get in yoo!!!", result.body) } + // the correct way with jwt @Test - fun testCreateOrder(){ - val result = restTemplate.postForEntity( + fun testCreateOrder(@Autowired jwtService: JwtService) { + //Mock + val token = jwtService.generateToken("meshal") + val headers = HttpHeaders( + MultiValueMap.fromSingleValue(mapOf("Authorization" to "Bearer $token")) + ) + val body = OrderRequest( + userId = 1, + items = listOf(Item("Chicken Burger", 3)) + ) + + //Trigger + val requestEntity = HttpEntity(body, headers) + val actualResponse = restTemplate.exchange( "/orders", //Endpoint - OrderRequest(1, listOf()), //Request body - String::class.java // Response body + HttpMethod.POST, + requestEntity, + OrderResponse::class.java ) - assertEquals(HttpStatus.OK, result.statusCode) + + //Assert + assertEquals(HttpStatus.OK, actualResponse.statusCode) + + val expectedResponse = OrderResponse( + userId = 1, + items = listOf( + Item("Chicken Burger", 3) + ) + ) + assertEquals(expectedResponse, actualResponse.body, "Unexpected order created...") } } + + From ac7aee71aa8d6ee03b1622baeaa74867fed54fb2 Mon Sep 17 00:00:00 2001 From: Mishal Alquraini Date: Tue, 29 Apr 2025 18:05:33 +0300 Subject: [PATCH 08/10] did cache as well --- src/main/kotlin/com/coded/spring/Application.kt | 7 +++++++ .../coded/spring/authentication/SecurityConfig.kt | 2 +- .../com/coded/spring/ordering/MenuController.kt | 4 ++-- .../com/coded/spring/ordering/MenuService.kt | 15 ++++++++++++--- .../com/coded/spring/ordering/OrdersController.kt | 4 ++-- .../kotlin/com/coded/spring/pets/PetsService.kt | 2 ++ 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/coded/spring/Application.kt b/src/main/kotlin/com/coded/spring/Application.kt index 3fe2c8e..e1024ee 100644 --- a/src/main/kotlin/com/coded/spring/Application.kt +++ b/src/main/kotlin/com/coded/spring/Application.kt @@ -1,5 +1,8 @@ package com.coded.spring +import com.hazelcast.config.Config +import com.hazelcast.core.Hazelcast +import com.hazelcast.core.HazelcastInstance import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication @@ -8,4 +11,8 @@ class Application fun main(args: Array) { runApplication(*args) + applicationConfig.getMapConfig("Menu").setTimeToLiveSeconds(120) } + +val applicationConfig = Config("application-cache") +val serverCache: HazelcastInstance = Hazelcast.newHazelcastInstance(applicationConfig) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt index 9d10c32..a688500 100644 --- a/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt +++ b/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt @@ -44,7 +44,7 @@ class SecurityConf ( // it.defaultSuccessUrl("/Public/menu", true)} // .userDetailsService(userDetailsService) // return http.build() - it.requestMatchers("/auth/**", "/users/v1/**","/api-docs","/welcome").permitAll() + it.requestMatchers("/auth/**", "/users/v1/**","/api-docs","/welcome","/Public/**").permitAll() .anyRequest().authenticated() } .sessionManagement { diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuController.kt b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt index 954add0..8fe694e 100644 --- a/src/main/kotlin/com/coded/spring/ordering/MenuController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt @@ -13,10 +13,10 @@ class MenuController( ){ @GetMapping("/Public/menu") - fun sayUsers() = menuRepository.findAll() + fun sayUsers() = menuService.listMenu() - @PostMapping("/users/v1/create/menu") + @PostMapping("Public/users/v1/create/menu") fun menuAdd(@RequestBody requestMenu: RequestMenu): Any { // val newMenu = menuService.addMenu(request) return ResponseEntity.ok().body(menuService.addMenu(requestMenu)) diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuService.kt b/src/main/kotlin/com/coded/spring/ordering/MenuService.kt index 9d56315..8ba3b48 100644 --- a/src/main/kotlin/com/coded/spring/ordering/MenuService.kt +++ b/src/main/kotlin/com/coded/spring/ordering/MenuService.kt @@ -1,5 +1,6 @@ package com.coded.spring.ordering +import com.coded.spring.serverCache import org.springframework.stereotype.Service import java.math.BigDecimal @@ -15,16 +16,24 @@ class MenuService( return menuRepository.save(newMenu) } - fun listMenu(): List = menuRepository.findAll().map { + fun listMenu(): List{ + val cachedMenu = menuCache["Menu"] + if (cachedMenu?.size == 0 || cachedMenu == null){ + println("no new meals") + val menu = menuRepository.findAll().map { Menu( menuName = it.name, price = it.price - ) + ) } + menuCache.put("Menu", menu) + return menu + } + return cachedMenu } } - +val menuCache = serverCache.getMap>("Menu") data class Menu( diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt index 20b9793..fad6042 100644 --- a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt @@ -13,10 +13,10 @@ class ordersController( val itemsRepository: ItemsRepository ){ - @GetMapping("/orders/v1/list") + @GetMapping("Public/orders/v1/list") fun showUsers() = ordersRepository.findAll() - @PostMapping("/orders") + @PostMapping("Public/orders") fun sayOrder( @RequestBody request: OrderRequest) : OrderResponse { val result = ordersService.createOrder(request.userId, request.items) // return OrderResponse(result.userId, result.items) both ways r correct diff --git a/src/main/kotlin/com/coded/spring/pets/PetsService.kt b/src/main/kotlin/com/coded/spring/pets/PetsService.kt index 32a1f11..35478a6 100644 --- a/src/main/kotlin/com/coded/spring/pets/PetsService.kt +++ b/src/main/kotlin/com/coded/spring/pets/PetsService.kt @@ -1,6 +1,8 @@ package com.coded.spring.pets import jakarta.inject.Named +import com.hazelcast.logging.Logger + @Named From be755de213820a534a00bfbea6486d351ee6771f Mon Sep 17 00:00:00 2001 From: Mishal Alquraini Date: Tue, 29 Apr 2025 18:40:57 +0300 Subject: [PATCH 09/10] did the bonus as well --- .../coded/spring/ordering/MenuController.kt | 3 +- .../com/coded/spring/ordering/MenuService.kt | 35 ++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuController.kt b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt index 8fe694e..f74b8f6 100644 --- a/src/main/kotlin/com/coded/spring/ordering/MenuController.kt +++ b/src/main/kotlin/com/coded/spring/ordering/MenuController.kt @@ -4,6 +4,7 @@ import org.springframework.http.ResponseEntity 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.RequestParam import org.springframework.web.bind.annotation.RestController @RestController @@ -13,7 +14,7 @@ class MenuController( ){ @GetMapping("/Public/menu") - fun sayUsers() = menuService.listMenu() + fun sayUsers(@RequestParam(required = false) search: String?) = menuService.listMenu(search) @PostMapping("Public/users/v1/create/menu") diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuService.kt b/src/main/kotlin/com/coded/spring/ordering/MenuService.kt index 8ba3b48..f103cb4 100644 --- a/src/main/kotlin/com/coded/spring/ordering/MenuService.kt +++ b/src/main/kotlin/com/coded/spring/ordering/MenuService.kt @@ -7,8 +7,8 @@ import java.math.BigDecimal @Service class MenuService( val menuRepository: MenuRepository -){ - fun addMenu(request: RequestMenu) : MenuEntity{ +) { + fun addMenu(request: RequestMenu): MenuEntity { val newMenu = MenuEntity( name = request.menuName, price = request.price @@ -16,23 +16,32 @@ class MenuService( return menuRepository.save(newMenu) } - fun listMenu(): List{ + fun listMenu(search: String?): List { val cachedMenu = menuCache["Menu"] - if (cachedMenu?.size == 0 || cachedMenu == null){ + + val menu = if (cachedMenu.isNullOrEmpty()) { println("no new meals") - val menu = menuRepository.findAll().map { - Menu( - menuName = it.name, - price = it.price - ) } - menuCache.put("Menu", menu) - return menu + val freshMenu = menuRepository.findAll().map { + Menu( + menuName = it.name, + price = it.price + ) + } + menuCache.put("Menu", freshMenu) + freshMenu + } else { + cachedMenu } - return cachedMenu - } + return if (!search.isNullOrBlank()) { + menu.filter { it.menuName.trim().lowercase().contains(search.trim().lowercase()) } + } else { + menu + } + } } + val menuCache = serverCache.getMap>("Menu") From 0afce920951cb082e40ae207e7dce3b6c45afe56 Mon Sep 17 00:00:00 2001 From: mishalalquraini Date: Wed, 30 Apr 2025 20:44:50 +0300 Subject: [PATCH 10/10] not done yet will finish later. --- authentication/pom.xml | 15 ++++++++ .../authentication/CustomerDetailsService.kt | 0 .../JWT/AuthenticationController.kt | 17 ++++++++- .../JWT/JwtAuthenticationFilter.kt | 4 ++ .../kotlin}/authentication/JWT/JwtService.kt | 0 .../kotlin/authentication/Loggerfilter.kt | 38 +++++++++++++++++++ .../Profiles/ProfileController.kt | 0 .../authentication}/Profiles/ProfileEntity.kt | 0 .../Profiles/ProfilesRepository.kt | 1 - .../Profiles/ProfilesService.kt | 0 .../kotlin}/authentication/SecurityConfig.kt | 0 .../authentication}/users/UserService.kt | 5 +++ .../authentication}/users/UsersController.kt | 0 .../authentication}/users/UsersRepository.kt | 0 ordering/pom.xml | 17 +++++++++ .../kotlin/client/AuthenticationClient.kt | 35 +++++++++++++++++ .../main/kotlin}/ordering/MenuController.kt | 0 .../src/main/kotlin}/ordering/MenuEntity.kt | 0 .../main/kotlin}/ordering/MenuRepository.kt | 0 .../src/main/kotlin}/ordering/MenuService.kt | 0 .../main/kotlin}/ordering/OrdersController.kt | 0 .../main/kotlin}/ordering/OrdersRepository.kt | 0 .../main/kotlin}/ordering/OrdersService.kt | 0 .../main/kotlin}/ordering/itemsRepository.kt | 0 .../security/RemoteAuthenticationfilter.kt | 36 ++++++++++++++++++ .../main/kotlin/security/securityconfig.kt | 31 +++++++++++++++ pom.xml | 5 +++ 27 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 authentication/pom.xml rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin}/authentication/CustomerDetailsService.kt (100%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin}/authentication/JWT/AuthenticationController.kt (74%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin}/authentication/JWT/JwtAuthenticationFilter.kt (95%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin}/authentication/JWT/JwtService.kt (100%) create mode 100644 authentication/src/main/kotlin/authentication/Loggerfilter.kt rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin/authentication}/Profiles/ProfileController.kt (100%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin/authentication}/Profiles/ProfileEntity.kt (100%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin/authentication}/Profiles/ProfilesRepository.kt (81%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin/authentication}/Profiles/ProfilesService.kt (100%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin}/authentication/SecurityConfig.kt (100%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin/authentication}/users/UserService.kt (88%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin/authentication}/users/UsersController.kt (100%) rename {src/main/kotlin/com/coded/spring => authentication/src/main/kotlin/authentication}/users/UsersRepository.kt (100%) create mode 100644 ordering/pom.xml create mode 100644 ordering/src/main/kotlin/client/AuthenticationClient.kt rename {src/main/kotlin/com/coded/spring => ordering/src/main/kotlin}/ordering/MenuController.kt (100%) rename {src/main/kotlin/com/coded/spring => ordering/src/main/kotlin}/ordering/MenuEntity.kt (100%) rename {src/main/kotlin/com/coded/spring => ordering/src/main/kotlin}/ordering/MenuRepository.kt (100%) rename {src/main/kotlin/com/coded/spring => ordering/src/main/kotlin}/ordering/MenuService.kt (100%) rename {src/main/kotlin/com/coded/spring => ordering/src/main/kotlin}/ordering/OrdersController.kt (100%) rename {src/main/kotlin/com/coded/spring => ordering/src/main/kotlin}/ordering/OrdersRepository.kt (100%) rename {src/main/kotlin/com/coded/spring => ordering/src/main/kotlin}/ordering/OrdersService.kt (100%) rename {src/main/kotlin/com/coded/spring => ordering/src/main/kotlin}/ordering/itemsRepository.kt (100%) create mode 100644 ordering/src/main/kotlin/security/RemoteAuthenticationfilter.kt create mode 100644 ordering/src/main/kotlin/security/securityconfig.kt diff --git a/authentication/pom.xml b/authentication/pom.xml new file mode 100644 index 0000000..5ffcb72 --- /dev/null +++ b/authentication/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + + com.coded.spring + Ordering + 0.0.1-SNAPSHOT + + + authentication + + \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/authentication/CustomerDetailsService.kt b/authentication/src/main/kotlin/authentication/CustomerDetailsService.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/authentication/CustomerDetailsService.kt rename to authentication/src/main/kotlin/authentication/CustomerDetailsService.kt diff --git a/src/main/kotlin/com/coded/spring/authentication/JWT/AuthenticationController.kt b/authentication/src/main/kotlin/authentication/JWT/AuthenticationController.kt similarity index 74% rename from src/main/kotlin/com/coded/spring/authentication/JWT/AuthenticationController.kt rename to authentication/src/main/kotlin/authentication/JWT/AuthenticationController.kt index 276ced1..6d68589 100644 --- a/src/main/kotlin/com/coded/spring/authentication/JWT/AuthenticationController.kt +++ b/authentication/src/main/kotlin/authentication/JWT/AuthenticationController.kt @@ -1,9 +1,11 @@ package com.coded.spring.authentication.JWT +import com.coded.spring.users.UsersService 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.* +import java.security.Principal @RestController @@ -11,7 +13,8 @@ import org.springframework.web.bind.annotation.* class AuthenticationController( private val authenticationManager: AuthenticationManager, private val userDetailsService: UserDetailsService, - private val jwtService: JwtService + private val jwtService: JwtService, + private val usersService: UsersService ) { @PostMapping("/login") @@ -27,6 +30,14 @@ class AuthenticationController( throw UsernameNotFoundException("Invalid user request!") } } + @PostMapping("/check-token") + fun checkToken( + principal: Principal + ): CheckTokenResponse { + return CheckTokenResponse( + userId = usersService.findByUsername(principal.name) + ) + } } data class AuthenticationRequest( @@ -36,4 +47,8 @@ data class AuthenticationRequest( data class AuthenticationResponse( val token: String +) + +data class CheckTokenResponse( + val userId: Long ) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/authentication/JWT/JwtAuthenticationFilter.kt b/authentication/src/main/kotlin/authentication/JWT/JwtAuthenticationFilter.kt similarity index 95% rename from src/main/kotlin/com/coded/spring/authentication/JWT/JwtAuthenticationFilter.kt rename to authentication/src/main/kotlin/authentication/JWT/JwtAuthenticationFilter.kt index c6ccdae..131259d 100644 --- a/src/main/kotlin/com/coded/spring/authentication/JWT/JwtAuthenticationFilter.kt +++ b/authentication/src/main/kotlin/authentication/JWT/JwtAuthenticationFilter.kt @@ -1,7 +1,9 @@ package com.coded.spring.authentication.JWT +import jakarta.inject.Named import jakarta.servlet.FilterChain import jakarta.servlet.http.* +import org.springframework.context.annotation.Bean import org.springframework.security.authentication.UsernamePasswordAuthenticationToken import org.springframework.security.core.context.SecurityContextHolder import org.springframework.security.core.userdetails.UserDetailsService @@ -9,6 +11,8 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS import org.springframework.stereotype.Component import org.springframework.web.filter.OncePerRequestFilter + + @Component class JwtAuthenticationFilter( private val jwtService: JwtService, diff --git a/src/main/kotlin/com/coded/spring/authentication/JWT/JwtService.kt b/authentication/src/main/kotlin/authentication/JWT/JwtService.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/authentication/JWT/JwtService.kt rename to authentication/src/main/kotlin/authentication/JWT/JwtService.kt diff --git a/authentication/src/main/kotlin/authentication/Loggerfilter.kt b/authentication/src/main/kotlin/authentication/Loggerfilter.kt new file mode 100644 index 0000000..e048104 --- /dev/null +++ b/authentication/src/main/kotlin/authentication/Loggerfilter.kt @@ -0,0 +1,38 @@ +package com.coded.spring.authentication + +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter +import org.springframework.web.util.ContentCachingRequestWrapper +import org.springframework.web.util.ContentCachingResponseWrapper + + +@Component +class LoggingFilter: OncePerRequestFilter(){ + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + val cachedRequest = ContentCachingRequestWrapper(request) + val cachedResponse = ContentCachingResponseWrapper(response) + + filterChain.doFilter(cachedRequest, cachedResponse) + + logRequest(cachedRequest) + logResponse(cachedResponse) + cachedResponse.copyBodyToResponse() + } + + private fun logRequest(request: ContentCachingRequestWrapper) { + val requestBody = String(request.contentAsByteArray) + logger.info("Request: method=${request.method}, uri=${request.requestURI}, body=$requestBody") + } + + private fun logResponse(response: ContentCachingResponseWrapper) { + val responseBody = String(response.contentAsByteArray) + logger.info("Response: status=${response.status}, body=$responseBody") + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt b/authentication/src/main/kotlin/authentication/Profiles/ProfileController.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/Profiles/ProfileController.kt rename to authentication/src/main/kotlin/authentication/Profiles/ProfileController.kt diff --git a/src/main/kotlin/com/coded/spring/Profiles/ProfileEntity.kt b/authentication/src/main/kotlin/authentication/Profiles/ProfileEntity.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/Profiles/ProfileEntity.kt rename to authentication/src/main/kotlin/authentication/Profiles/ProfileEntity.kt diff --git a/src/main/kotlin/com/coded/spring/Profiles/ProfilesRepository.kt b/authentication/src/main/kotlin/authentication/Profiles/ProfilesRepository.kt similarity index 81% rename from src/main/kotlin/com/coded/spring/Profiles/ProfilesRepository.kt rename to authentication/src/main/kotlin/authentication/Profiles/ProfilesRepository.kt index f065933..fdd9c47 100644 --- a/src/main/kotlin/com/coded/spring/Profiles/ProfilesRepository.kt +++ b/authentication/src/main/kotlin/authentication/Profiles/ProfilesRepository.kt @@ -1,6 +1,5 @@ package com.coded.spring.Profiles -import com.coded.spring.ordering.MenuEntity import jakarta.inject.Named import org.springframework.data.jpa.repository.JpaRepository diff --git a/src/main/kotlin/com/coded/spring/Profiles/ProfilesService.kt b/authentication/src/main/kotlin/authentication/Profiles/ProfilesService.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/Profiles/ProfilesService.kt rename to authentication/src/main/kotlin/authentication/Profiles/ProfilesService.kt diff --git a/src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt b/authentication/src/main/kotlin/authentication/SecurityConfig.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/authentication/SecurityConfig.kt rename to authentication/src/main/kotlin/authentication/SecurityConfig.kt diff --git a/src/main/kotlin/com/coded/spring/users/UserService.kt b/authentication/src/main/kotlin/authentication/users/UserService.kt similarity index 88% rename from src/main/kotlin/com/coded/spring/users/UserService.kt rename to authentication/src/main/kotlin/authentication/users/UserService.kt index 86cb106..32863c2 100644 --- a/src/main/kotlin/com/coded/spring/users/UserService.kt +++ b/authentication/src/main/kotlin/authentication/users/UserService.kt @@ -31,6 +31,11 @@ class UsersService( ) } + fun findByUsername(name: String): Long { + return usersRepository.findByUsername(name)?.id ?: + throw IllegalArgumentException("username not found") + } + } diff --git a/src/main/kotlin/com/coded/spring/users/UsersController.kt b/authentication/src/main/kotlin/authentication/users/UsersController.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/users/UsersController.kt rename to authentication/src/main/kotlin/authentication/users/UsersController.kt diff --git a/src/main/kotlin/com/coded/spring/users/UsersRepository.kt b/authentication/src/main/kotlin/authentication/users/UsersRepository.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/users/UsersRepository.kt rename to authentication/src/main/kotlin/authentication/users/UsersRepository.kt diff --git a/ordering/pom.xml b/ordering/pom.xml new file mode 100644 index 0000000..bb6a78e --- /dev/null +++ b/ordering/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + com.coded.spring + Ordering + 0.0.1-SNAPSHOT + + + ordering + + + + + \ No newline at end of file diff --git a/ordering/src/main/kotlin/client/AuthenticationClient.kt b/ordering/src/main/kotlin/client/AuthenticationClient.kt new file mode 100644 index 0000000..e1dab33 --- /dev/null +++ b/ordering/src/main/kotlin/client/AuthenticationClient.kt @@ -0,0 +1,35 @@ +package com.coded.spring.client + +import jakarta.inject.Named +import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.HttpEntity +import org.springframework.http.HttpMethod +import org.springframework.util.MultiValueMap +import org.springframework.web.client.RestTemplate +import org.springframework.web.client.exchange + + +@Named +class AuthenticationClient { + + fun checkToken(token: String): CheckTokenResponse { + val restTemplate = RestTemplate() + val url = "http://localhost:8080/authentication/v1/check-token" + val response = restTemplate.exchange( + url = url, + method = HttpMethod.POST, + requestEntity = HttpEntity( + MultiValueMap.fromMultiValue(mapOf("Authorization" to listOf("Bearer $token"))) + ), + object : ParameterizedTypeReference() { + } + ) + return response.body ?: throw IllegalStateException("Check token response has no body ...") + } + +} + + +data class CheckTokenResponse( + val userId: Long +) \ No newline at end of file diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuController.kt b/ordering/src/main/kotlin/ordering/MenuController.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/ordering/MenuController.kt rename to ordering/src/main/kotlin/ordering/MenuController.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuEntity.kt b/ordering/src/main/kotlin/ordering/MenuEntity.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/ordering/MenuEntity.kt rename to ordering/src/main/kotlin/ordering/MenuEntity.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuRepository.kt b/ordering/src/main/kotlin/ordering/MenuRepository.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/ordering/MenuRepository.kt rename to ordering/src/main/kotlin/ordering/MenuRepository.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/MenuService.kt b/ordering/src/main/kotlin/ordering/MenuService.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/ordering/MenuService.kt rename to ordering/src/main/kotlin/ordering/MenuService.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersController.kt b/ordering/src/main/kotlin/ordering/OrdersController.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/ordering/OrdersController.kt rename to ordering/src/main/kotlin/ordering/OrdersController.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt b/ordering/src/main/kotlin/ordering/OrdersRepository.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/ordering/OrdersRepository.kt rename to ordering/src/main/kotlin/ordering/OrdersRepository.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/OrdersService.kt b/ordering/src/main/kotlin/ordering/OrdersService.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/ordering/OrdersService.kt rename to ordering/src/main/kotlin/ordering/OrdersService.kt diff --git a/src/main/kotlin/com/coded/spring/ordering/itemsRepository.kt b/ordering/src/main/kotlin/ordering/itemsRepository.kt similarity index 100% rename from src/main/kotlin/com/coded/spring/ordering/itemsRepository.kt rename to ordering/src/main/kotlin/ordering/itemsRepository.kt diff --git a/ordering/src/main/kotlin/security/RemoteAuthenticationfilter.kt b/ordering/src/main/kotlin/security/RemoteAuthenticationfilter.kt new file mode 100644 index 0000000..25a6e96 --- /dev/null +++ b/ordering/src/main/kotlin/security/RemoteAuthenticationfilter.kt @@ -0,0 +1,36 @@ +package com.coded.spring.security + +import com.coded.spring.client.AuthenticationClient +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.stereotype.Component +import org.springframework.web.filter.OncePerRequestFilter +import kotlin.text.startsWith +import kotlin.text.substring + + +@Component +class RemoteAuthenticationFilter( + private val authenticationClient: AuthenticationClient, +) : OncePerRequestFilter() { + + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain + ) { + logger.info("Remote authentication filter running...") + val authHeader = request.getHeader("Authorization") + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response) + return + } + + val token = authHeader.substring(7) + val result = authenticationClient.checkToken(token) + request.setAttribute("userId", result.userId) + + filterChain.doFilter(request, response) + } +} \ No newline at end of file diff --git a/ordering/src/main/kotlin/security/securityconfig.kt b/ordering/src/main/kotlin/security/securityconfig.kt new file mode 100644 index 0000000..70390c0 --- /dev/null +++ b/ordering/src/main/kotlin/security/securityconfig.kt @@ -0,0 +1,31 @@ +package com.coded.spring.security + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +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.web.SecurityFilterChain +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter + + +@Configuration +@EnableWebSecurity +class SecurityConfig( + private val remoteAuthFilter: RemoteAuthenticationFilter +) { + + @Bean + fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + http.csrf { it.disable() } + .authorizeHttpRequests { + it.anyRequest().permitAll() + } + .sessionManagement { + it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) + } + .addFilterBefore(remoteAuthFilter, UsernamePasswordAuthenticationFilter::class.java) + + return http.build() + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index ee50dba..087efac 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ com.coded.spring Ordering 0.0.1-SNAPSHOT + pom Kotlin.SpringbootV2 Kotlin.SpringbootV2 @@ -20,6 +21,10 @@ + + authentication + ordering +