Skip to content

Commit

Permalink
fix: todos
Browse files Browse the repository at this point in the history
  • Loading branch information
y9vad9 committed Dec 13, 2023
1 parent db02fc4 commit 31bee2f
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.timemates.api.rsocket.auth

import io.rsocket.kotlin.payload.Payload
import io.timemates.backend.authorization.usecases.GetUserIdByAccessTokenUseCase
import io.timemates.backend.authorization.usecases.GetAuthorizationUseCase
import io.timemates.rsproto.metadata.ExtraMetadata
import io.timemates.rsproto.server.annotations.ExperimentalInterceptorsApi
import io.timemates.rsproto.server.interceptors.Interceptor
Expand All @@ -14,17 +14,17 @@ import kotlin.coroutines.CoroutineContext
*/
@OptIn(ExperimentalInterceptorsApi::class)
class AuthInterceptor(
private val getUserIdByAccessTokenUseCase: GetUserIdByAccessTokenUseCase,
private val getAuthorizationUseCase: GetAuthorizationUseCase,
) : Interceptor() {
data class Data(val accessHash: String?, val userIdProvider: GetUserIdByAccessTokenUseCase) : CoroutineContext.Element {
data class Data(val accessHash: String?, val authorizationProvider: GetAuthorizationUseCase) : CoroutineContext.Element {
override val key get() = Key

companion object Key : CoroutineContext.Key<Data>
}

override fun intercept(coroutineContext: CoroutineContext, incoming: Payload): CoroutineContext {
val accessHash = coroutineContext[ExtraMetadata]?.extra?.get("access_hash")?.decodeToString()
return coroutineContext + Data(accessHash, getUserIdByAccessTokenUseCase)
return coroutineContext + Data(accessHash, getAuthorizationUseCase)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class AuthorizationService(
}

override suspend fun terminateAuthorization(request: Empty): Empty {
removeAccessTokenUseCase.execute(AccessHash.createOrFail(Request.userAccessHash()))
removeAccessTokenUseCase.execute(AccessHash.createOrFail(Request.userAccessHash() ?: unauthorized()))
return Empty.Default
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ internal fun alreadyExists(): Nothing = throw ApiFailure.AlreadyExists
context(RSocketService)
internal fun noAccess(): Nothing = throw ApiFailure.NoAccess

context(RSocketService)
internal fun unauthorized(): Nothing = throw ApiFailure.Unauthorized

internal object ApiFailure {
/**
* Variable for custom RSocket error when the number of attempts exceeds a limit.
Expand Down Expand Up @@ -68,4 +71,10 @@ internal object ApiFailure {
*/
val NoAccess: RSocketError.Custom
get() = RSocketError.Custom(HttpStatusCode.Forbidden.value, "No access to given resource or operation.")

/**
* Represents unauthorized failure in requests.
*/
val Unauthorized: RSocketError.Custom
get() = RSocketError.Custom(HttpStatusCode.Unauthorized.value, "Unauthorized.")
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package io.timemates.api.rsocket.internal

import io.timemates.api.rsocket.auth.AuthInterceptor
import io.timemates.backend.authorization.types.value.AccessHash
import io.timemates.backend.authorization.usecases.GetAuthorizationUseCase
import io.timemates.backend.features.authorization.Authorized
import io.timemates.backend.features.authorization.AuthorizedContext
import io.timemates.backend.features.authorization.Scope
import io.timemates.backend.features.authorization.authorizationProvider
import io.timemates.backend.features.authorization.types.AuthorizedId
import io.timemates.rsproto.server.RSocketService
import kotlinx.coroutines.currentCoroutineContext

/**
* Executes the provided block of code within an authorized context.
Expand All @@ -11,6 +18,21 @@ import io.timemates.rsproto.server.RSocketService
* @return The result of executing the code block.
*/
context(RSocketService)
internal suspend inline fun <T : Scope, R> authorized(block: AuthorizedContext<T>.() -> R): R {
TODO()
internal suspend inline fun <reified T : Scope, R> authorized(
constraint: (List<Scope>) -> Boolean = { scopes -> scopes.any { it is T || it is Scope.All } },
block: AuthorizedContext<T>.() -> R,
): R {
val authInfo = currentCoroutineContext()[AuthInterceptor.Data] ?: unauthorized()
val accessHash = authInfo.accessHash ?: unauthorized()

return authorizationProvider(
provider = {
authInfo.authorizationProvider.execute(AccessHash.createOrFail(accessHash))
.let { (it as? GetAuthorizationUseCase.Result.Success)?.authorization ?: unauthorized() }
.takeIf { constraint(it.scopes) }
?.let { Authorized(AuthorizedId(it.userId.long), scopes = it.scopes) }
},
onFailure = { unauthorized() },
block = block,
)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.timemates.api.rsocket.internal

import io.rsocket.kotlin.RSocketError
import io.timemates.api.rsocket.auth.AuthInterceptor
import io.timemates.backend.validation.SafeConstructor
import io.timemates.backend.validation.ValidationFailureHandler
import io.timemates.rsproto.server.RSocketService
import kotlinx.coroutines.currentCoroutineContext

/**
* Used as a handler for validation inside RSocket requests.
Expand Down Expand Up @@ -35,5 +37,5 @@ internal fun <T, W> SafeConstructor<T, W>.createOrFail(value: W): T {

object Request {
context(RSocketService)
internal fun userAccessHash(): String = TODO()
internal suspend fun userAccessHash(): String? = currentCoroutineContext()[AuthInterceptor.Data]?.accessHash
}

0 comments on commit 31bee2f

Please sign in to comment.