дописала security
This commit is contained in:
+2
-2
@@ -49,8 +49,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.spring.grpc.starter)
|
implementation(libs.spring.grpc.starter)
|
||||||
implementation(libs.grpc.services)
|
implementation(libs.grpc.services)
|
||||||
// Temporarily disabled due to OAuth2 configuration issues
|
|
||||||
// implementation(libs.spring.boot.starter.oauth2.client)
|
implementation(libs.spring.boot.starter.oauth2.client)
|
||||||
|
|
||||||
runtimeOnly(libs.micrometer.registry.prometheus)
|
runtimeOnly(libs.micrometer.registry.prometheus)
|
||||||
runtimeOnly(libs.h2)
|
runtimeOnly(libs.h2)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAu
|
|||||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
|
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
|
|
||||||
@SpringBootApplication(exclude = [OAuth2ClientAutoConfiguration::class])
|
@SpringBootApplication
|
||||||
@ConfigurationPropertiesScan("com.project.movienight.config")
|
@ConfigurationPropertiesScan("com.project.movienight.config")
|
||||||
class MovieNightApplication
|
class MovieNightApplication
|
||||||
|
|
||||||
|
|||||||
+50
-38
@@ -9,6 +9,7 @@ import com.project.movienight.domain.model.User
|
|||||||
import org.springframework.jdbc.core.JdbcTemplate
|
import org.springframework.jdbc.core.JdbcTemplate
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
import java.sql.ResultSet
|
import java.sql.ResultSet
|
||||||
|
import java.time.LocalDateTime
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
@@ -27,18 +28,32 @@ class UserRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun save(user: User): User {
|
override fun save(user: User): User {
|
||||||
val entity = user.toEntity()
|
val existingUser = findById(user.id)
|
||||||
val updatedRows =
|
|
||||||
jdbc.update(
|
val entity = if (existingUser != null) {
|
||||||
"""
|
val existingEntity = existingUser.toEntity()
|
||||||
UPDATE users
|
user.toEntity(
|
||||||
SET name = ?, email = ?
|
provider = existingEntity.provider?.let { AuthProvider.valueOf(it) },
|
||||||
WHERE id = ?
|
providerId = existingEntity.providerId,
|
||||||
""".trimIndent(),
|
createdAt = existingEntity.createdAt
|
||||||
entity.name,
|
|
||||||
entity.email,
|
|
||||||
entity.id,
|
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
user.toEntity()
|
||||||
|
}
|
||||||
|
|
||||||
|
val updatedRows = jdbc.update(
|
||||||
|
"""
|
||||||
|
UPDATE users
|
||||||
|
SET name = ?, email = ?, provider = ?, provider_id = ?
|
||||||
|
WHERE id = ?
|
||||||
|
""".trimIndent(),
|
||||||
|
entity.name,
|
||||||
|
entity.email,
|
||||||
|
entity.provider,
|
||||||
|
entity.providerId,
|
||||||
|
entity.id,
|
||||||
|
)
|
||||||
|
|
||||||
if (updatedRows == 0) {
|
if (updatedRows == 0) {
|
||||||
jdbc.update(
|
jdbc.update(
|
||||||
"""
|
"""
|
||||||
@@ -57,31 +72,28 @@ class UserRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun findById(id: UUID): User? {
|
override fun findById(id: UUID): User? {
|
||||||
val entities =
|
val entities = jdbc.query(
|
||||||
jdbc.query(
|
"SELECT id, name, email, provider, provider_id, created_at FROM users WHERE id = ?",
|
||||||
"SELECT id, name, email, provider, provider_id, created_at FROM users WHERE id = ?",
|
userEntityRowMapper,
|
||||||
userEntityRowMapper,
|
id,
|
||||||
id,
|
)
|
||||||
)
|
|
||||||
return entities.firstOrNull()?.toDomain()
|
return entities.firstOrNull()?.toDomain()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findByEmail(email: String): User? {
|
override fun findByEmail(email: String): User? {
|
||||||
val entities =
|
val entities = jdbc.query(
|
||||||
jdbc.query(
|
"SELECT id, name, email, provider, provider_id, created_at FROM users WHERE email = ?",
|
||||||
"SELECT id, name, email, provider, provider_id, created_at FROM users WHERE email = ?",
|
userEntityRowMapper,
|
||||||
userEntityRowMapper,
|
email,
|
||||||
email,
|
)
|
||||||
)
|
|
||||||
return entities.firstOrNull()?.toDomain()
|
return entities.firstOrNull()?.toDomain()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findAll(): List<User> =
|
override fun findAll(): List<User> =
|
||||||
jdbc
|
jdbc.query(
|
||||||
.query(
|
"SELECT id, name, email, provider, provider_id, created_at FROM users",
|
||||||
"SELECT id, name, email, provider, provider_id, created_at FROM users",
|
userEntityRowMapper,
|
||||||
userEntityRowMapper,
|
).map { it.toDomain() }
|
||||||
).map { it.toDomain() }
|
|
||||||
|
|
||||||
override fun deleteById(id: UUID) {
|
override fun deleteById(id: UUID) {
|
||||||
jdbc.update("DELETE FROM users WHERE id = ?", id)
|
jdbc.update("DELETE FROM users WHERE id = ?", id)
|
||||||
@@ -91,16 +103,16 @@ class UserRepository(
|
|||||||
provider: AuthProvider,
|
provider: AuthProvider,
|
||||||
providerId: String,
|
providerId: String,
|
||||||
): User? {
|
): User? {
|
||||||
val entities =
|
val entities = jdbc.query(
|
||||||
jdbc.query(
|
"""
|
||||||
"""
|
SELECT id, name, email, provider, provider_id, created_at
|
||||||
SELECT id, name, email, provider, provider_id, created_at FROM users
|
FROM users
|
||||||
WHERE provider = ? AND provider_id = ?
|
WHERE provider = ? AND provider_id = ?
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
userEntityRowMapper,
|
userEntityRowMapper,
|
||||||
provider.name,
|
provider.name,
|
||||||
providerId,
|
providerId,
|
||||||
)
|
)
|
||||||
return entities.firstOrNull()?.toDomain()
|
return entities.firstOrNull()?.toDomain()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package com.project.movienight.adapters.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.web.SecurityFilterChain
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
class SecurityConfiguration(
|
||||||
|
private val customOAuth2UserService: CustomOAuth2UserService,
|
||||||
|
) {
|
||||||
|
@Bean
|
||||||
|
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||||
|
http
|
||||||
|
.oauth2Login { oauth2 ->
|
||||||
|
oauth2
|
||||||
|
.userInfoEndpoint { userInfo ->
|
||||||
|
userInfo.userService(customOAuth2UserService)
|
||||||
|
}
|
||||||
|
.defaultSuccessUrl("/api/users/me", true)
|
||||||
|
}
|
||||||
|
.authorizeHttpRequests { auth ->
|
||||||
|
auth
|
||||||
|
.requestMatchers("/", "/login/**", "/oauth2/**", "/h2-console/**", "/actuator/health").permitAll()
|
||||||
|
.requestMatchers("/api/users/me").authenticated()
|
||||||
|
.requestMatchers("/api/**").authenticated()
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
}
|
||||||
|
.headers { headers ->
|
||||||
|
headers.frameOptions { frameOptions ->
|
||||||
|
frameOptions.sameOrigin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.csrf { csrf ->
|
||||||
|
csrf.disable()
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,15 +10,16 @@ import java.util.UUID
|
|||||||
class UserPrincipal(
|
class UserPrincipal(
|
||||||
private val user: User,
|
private val user: User,
|
||||||
private val attributes: Map<String, Any>? = null,
|
private val attributes: Map<String, Any>? = null,
|
||||||
) : OAuth2User,
|
) : OAuth2User, UserDetails {
|
||||||
UserDetails {
|
|
||||||
fun getId(): UUID = user.id
|
fun getId(): UUID = user.id
|
||||||
|
|
||||||
override fun getName(): String = user.name
|
override fun getName(): String = user.name
|
||||||
|
|
||||||
override fun getAttributes(): Map<String, Any> = attributes ?: emptyMap()
|
override fun getAttributes(): Map<String, Any> = attributes ?: emptyMap()
|
||||||
|
|
||||||
override fun getAuthorities(): Collection<GrantedAuthority> = listOf(SimpleGrantedAuthority("ROLE_USER"))
|
override fun getAuthorities(): Collection<GrantedAuthority> =
|
||||||
|
listOf(SimpleGrantedAuthority("ROLE_USER"))
|
||||||
|
|
||||||
override fun getPassword(): String = ""
|
override fun getPassword(): String = ""
|
||||||
|
|
||||||
@@ -33,9 +34,7 @@ class UserPrincipal(
|
|||||||
override fun isEnabled(): Boolean = true
|
override fun isEnabled(): Boolean = true
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun create(
|
fun create(user: User, attributes: Map<String, Any>? = null): UserPrincipal =
|
||||||
user: User,
|
UserPrincipal(user, attributes)
|
||||||
attributes: Map<String, Any>? = null,
|
|
||||||
): UserPrincipal = UserPrincipal(user, attributes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestBody
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus
|
import org.springframework.web.bind.annotation.ResponseStatus
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
//import com.project.movienight.adapters.security.UserPrincipal
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@@ -73,4 +74,12 @@ class UserController(
|
|||||||
fun delete(
|
fun delete(
|
||||||
@PathVariable id: UUID,
|
@PathVariable id: UUID,
|
||||||
) = deleteUserUseCase.delete(id)
|
) = deleteUserUseCase.delete(id)
|
||||||
|
|
||||||
|
/*
|
||||||
|
@GetMapping("/me")
|
||||||
|
fun getCurrentUser(principal: UserPrincipal): UserResponse =
|
||||||
|
UserResponse.fromDomain(
|
||||||
|
getUserByIdUseCase.getById(principal.getId())
|
||||||
|
)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,38 @@ spring:
|
|||||||
console:
|
console:
|
||||||
enabled: ${SPRING_H2_CONSOLE_ENABLED:true}
|
enabled: ${SPRING_H2_CONSOLE_ENABLED:true}
|
||||||
path: /h2-console
|
path: /h2-console
|
||||||
|
security:
|
||||||
|
oauth2:
|
||||||
|
client:
|
||||||
|
registration:
|
||||||
|
google:
|
||||||
|
client-id: ${OAUTH2_GOOGLE_CLIENT_ID:test-client-id}
|
||||||
|
client-secret: ${OAUTH2_GOOGLE_CLIENT_SECRET:test-secret}
|
||||||
|
scope: email,profile
|
||||||
|
yandex:
|
||||||
|
client-id: ${OAUTH2_YANDEX_CLIENT_ID:test-client-id}
|
||||||
|
client-secret: ${OAUTH2_YANDEX_CLIENT_SECRET:test-secret}
|
||||||
|
authorization-grant-type: authorization_code
|
||||||
|
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
|
||||||
|
scope: login:email,login:avatar
|
||||||
|
vk:
|
||||||
|
client-id: ${OAUTH2_VK_CLIENT_ID:test-client-id}
|
||||||
|
client-secret: ${OAUTH2_VK_CLIENT_SECRET:test-secret}
|
||||||
|
authorization-grant-type: authorization_code
|
||||||
|
client-authentication-method: client_secret_post
|
||||||
|
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
|
||||||
|
scope: email
|
||||||
|
provider:
|
||||||
|
yandex:
|
||||||
|
authorization-uri: https://oauth.yandex.ru/authorize
|
||||||
|
token-uri: https://oauth.yandex.ru/token
|
||||||
|
user-info-uri: https://login.yandex.ru/info
|
||||||
|
user-name-attribute: id
|
||||||
|
vk:
|
||||||
|
authorization-uri: https://oauth.vk.com/authorize
|
||||||
|
token-uri: https://oauth.vk.com/access_token
|
||||||
|
user-info-uri: https://api.vk.com/method/users.get?v=5.131&fields=photo_200
|
||||||
|
user-name-attribute: response
|
||||||
|
|
||||||
server:
|
server:
|
||||||
shutdown: graceful
|
shutdown: graceful
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_provider_provider_id
|
||||||
|
ON users(provider, provider_id)
|
||||||
|
WHERE provider IS NOT NULL AND provider_id IS NOT NULL;
|
||||||
Reference in New Issue
Block a user