Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#144 Deliver prometheus auth token via env #163

Merged
merged 3 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package ru.vityaman.lms.botalka.app.spring.security

import kotlinx.coroutines.reactor.mono
import org.springframework.security.authentication.ReactiveAuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.GrantedAuthority
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono
import ru.vityaman.lms.botalka.core.exception.AuthenticationException
import ru.vityaman.lms.botalka.core.security.auth.AccessToken
import ru.vityaman.lms.botalka.core.security.auth.Authentication
import ru.vityaman.lms.botalka.core.security.auth.TokenGuard
import org.springframework.security.core.Authentication as SpringAuthentication

@Component
class SpringAuthenticationManager(private val guard: TokenGuard) :
ReactiveAuthenticationManager {
override fun authenticate(
authentication: SpringAuthentication,
): Mono<SpringAuthentication> = mono { doAuthenticate(authentication) }

private suspend fun doAuthenticate(
authentication: SpringAuthentication,
): SpringAuthentication {
val credentials = authentication.credentials.toString()
val token = AccessToken(credentials)
val auth = guard.authenticate(token)
?: throw AuthenticationException("unknown auth token")
return converted(auth)
}

private fun converted(auth: Authentication): SpringAuthentication {
val roles = emptyList<GrantedAuthority>()
return when (auth) {
is Authentication.Client -> {
UsernamePasswordAuthenticationToken(auth.user, null, roles)
}

is Authentication.Fuzzer -> {
UsernamePasswordAuthenticationToken(auth.user, null, roles)
}

Authentication.Monitoring -> {
UsernamePasswordAuthenticationToken(null, null, null)
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import ru.vityaman.lms.botalka.core.exception.DomainException
@Component
class SpringJwtContextRepository(
private val marshalling: DomainExceptionMarshalling,
private val authManager: SpringJwtAuthManager,
private val authManager: SpringAuthenticationManager,
) : ServerSecurityContextRepository {
private val headerName = HttpHeaders.AUTHORIZATION
private val bearerPrefix = "Bearer "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ private typealias And = AndServerWebExchangeMatcher
@Configuration
@EnableWebFluxSecurity
class SpringSecurityConfig(
private val authManager: SpringJwtAuthManager,
private val authManager: SpringAuthenticationManager,
private val contextRepository: SpringJwtContextRepository,
) {
@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ru.vityaman.lms.botalka.app.spring.security

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import ru.vityaman.lms.botalka.core.logic.UserService
import ru.vityaman.lms.botalka.core.security.auth.CombinedTokenGuard
import ru.vityaman.lms.botalka.core.security.auth.JwtClientTokenGuard
import ru.vityaman.lms.botalka.core.security.auth.MonitoringTokenGuard
import ru.vityaman.lms.botalka.core.security.auth.TokenGuard
import ru.vityaman.lms.botalka.core.security.auth.TokenService

@Component
class SpringTokenGuard(
tokens: TokenService,
users: UserService,

@Value("\${security.token.special.monitoring}")
monitoringToken: String,
) : TokenGuard by CombinedTokenGuard(
MonitoringTokenGuard(monitoringToken),
JwtClientTokenGuard(tokens, users),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package ru.vityaman.lms.botalka.core.security.auth

import ru.vityaman.lms.botalka.core.exception.AuthenticationException
import ru.vityaman.lms.botalka.core.exception.orNotFound
import ru.vityaman.lms.botalka.core.logic.UserService
import ru.vityaman.lms.botalka.core.model.User

sealed class Authentication {
data object Monitoring : Authentication()
data class Fuzzer(val user: User) : Authentication()
data class Client(val user: User) : Authentication()
}

interface TokenGuard {
suspend fun authenticate(token: AccessToken): Authentication?
}

class MonitoringTokenGuard(private val reference: String) : TokenGuard {
override suspend fun authenticate(
token: AccessToken,
): Authentication.Monitoring? =
if (token.text == reference) {
Authentication.Monitoring
} else {
null
}
}

class JwtClientTokenGuard(
private val tokens: TokenService,
private val users: UserService,
) : TokenGuard {
override suspend fun authenticate(
token: AccessToken,
): Authentication.Client {
val payload = tokens.decode(token)
val user = users.getById(payload.userId)
.orNotFound("user with id ${payload.userId}")
return Authentication.Client(user)
}
}

class CombinedTokenGuard(vararg guards: TokenGuard) : TokenGuard {
private val guards = guards.toList()

override suspend fun authenticate(token: AccessToken): Authentication {
for (guard in guards) {
val auth = guard.authenticate(token)
if (auth != null) {
return auth
}
}
throw AuthenticationException("Unknown authentication token kind")
}
}
2 changes: 2 additions & 0 deletions botalka/src/main/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ security:
token:
signing:
secret: lms0security0token0signing0key0secret0very0very0very0long
special:
monitoring: prometheus-top-top-top-secret-token
external:
service:
yandex:
Expand Down
2 changes: 2 additions & 0 deletions botalka/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ security:
signing:
secret: ${LMS_SECURITY_TOKEN_SIGNING_SECRET}
duration: PT2H
special:
monitoring: ${LMS_PROMETHEUS_ACCESS_TOKEN}
external:
service:
yandex:
Expand Down
1 change: 1 addition & 0 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ services:
LMS_SECURITY_TOKEN_SIGNING_SECRET: ${LMS_SECURITY_TOKEN_SIGNING_SECRET?:err}
LMS_TEST_TELEGRAM_BOT_API_TOKEN: ${LMS_TEST_TELEGRAM_BOT_API_TOKEN?:err}
LMS_TEST_TELEGRAM_ADMIN_CHAT_ID: ${LMS_TEST_TELEGRAM_ADMIN_CHAT_ID?:err}
LMS_PROMETHEUS_ACCESS_TOKEN: ${LMS_PROMETHEUS_ACCESS_TOKEN?:err}
networks:
- lms-network
depends_on:
Expand Down
7 changes: 7 additions & 0 deletions infra/env/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cd "$(dirname "$0")"/../.. || exit

. ./infra/env/local.sh
. ./infra/env/secret.sh

LMS_PROMETHEUS_ACCESS_TOKEN="$(bash ./infra/prometheus/extract_token.bash)"
export LMS_PROMETHEUS_ACCESS_TOKEN
9 changes: 9 additions & 0 deletions infra/prometheus/extract_token.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

set -e

cd "$(dirname "$0")"/../.. || exit

TOKEN="$(awk '$1=="credentials:"{print $2}' infra/prometheus/prometheus.yml)"
TOKEN="${TOKEN:1:${#TOKEN} - 2}"
echo "$TOKEN"
Loading