Kotlin Ktor client 403 forbidden cloudflare - cloudflare

So im trying to request a side with proxies that is protected with cloudflare. The problem is i get 403 forbidden cloduflare error but only when im using proxies without it works. But the proxies are not the problem i tried them with python(requests module) and in my browser there i dont get blocked. My code
suspend fun scrape() {
val client = HttpClient {
followRedirects = true
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
})
}
engine {
proxy =
ProxyBuilder.http("http://ProxyIP:proxyPort")
}
defaultRequest {
val credentials = Base64.getEncoder().encodeToString("ProxyUser:ProxyPassword".toByteArray())
header(HttpHeaders.ProxyAuthorization, "Basic $credentials")
}
}
val response = client.get("http://example.com")
val body = response.bodyAsText()
println(body)
println(response.status.hashCode())
Fixxed it
suspend fun scrape() {
val client = HttpClient(Apache) {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
})
}
engine {
followRedirects = false
customizeClient {
setProxy(HttpHost("hostname", port))
val credentialsProvider = BasicCredentialsProvider()
credentialsProvider .setCredentials(
AuthScope("hostname", port),
UsernamePasswordCredentials("username", "password")
)
setDefaultCredentialsProvider(credentialsProvider )
}
}
}
val response =
client.get("http://example.com") {
}
val body = response.bodyAsText()
println(body)
println(response.status.hashCode())
}

There is a problem with making a request through a proxy server using the CIO engine. I've created an issue to address this problem. As a workaround, please use the OkHttp engine instead of CIO.
Here is how you can use a proxy with the Basic authentication:
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.request.*
import kotlinx.coroutines.runBlocking
import okhttp3.Authenticator
import okhttp3.Credentials
import okhttp3.OkHttpClient
import java.net.Proxy
fun main(): Unit = runBlocking {
val proxyAuthenticator = Authenticator { _, response ->
response.request.newBuilder()
.header("Proxy-Authorization", Credentials.basic("<username>", "<password>"))
.build()
}
val client = HttpClient(OkHttp) {
engine {
preconfigured = OkHttpClient.Builder()
.proxy(Proxy(Proxy.Type.HTTP, java.net.InetSocketAddress("127.0.0.1", 3128)))
.proxyAuthenticator(proxyAuthenticator)
.build()
}
}
val response = client.get("http://eu.kith.com/products.json")
println(response.status)
}

Related

How to use firebase admin with ktor 2.0

Does anyone have an idea how to convert these codes to ktor 2.0.
https://gist.github.com/togisoft/d1113a83eeb1d6b52031f77fe780ce48
If someone needs to see a complete sample of Aleksei's answer, I created a sample repository.
I did make some slight tweaks to the other answer as the implementation error was missing the correct messaging from the original gist.
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseToken
import io.ktor.http.auth.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class FirebaseAuthProvider(config: FirebaseConfig): AuthenticationProvider(config) {
val authHeader: (ApplicationCall) -> HttpAuthHeader? = config.authHeader
private val authFunction = config.firebaseAuthenticationFunction
override suspend fun onAuthenticate(context: AuthenticationContext) {
val token = authHeader(context.call)
if (token == null) {
context.challenge(FirebaseJWTAuthKey, AuthenticationFailedCause.InvalidCredentials) { challengeFunc, call ->
challengeFunc.complete()
call.respond(UnauthorizedResponse(HttpAuthHeader.bearerAuthChallenge(realm = FIREBASE_AUTH)))
}
return
}
try {
val principal = verifyFirebaseIdToken(context.call, token, authFunction)
if (principal != null) {
context.principal(principal)
}
} catch (cause: Throwable) {
val message = cause.message ?: cause.javaClass.simpleName
context.error(FirebaseJWTAuthKey, AuthenticationFailedCause.Error(message))
}
}
}
class FirebaseConfig(name: String?) : AuthenticationProvider.Config(name) {
internal var authHeader: (ApplicationCall) -> HttpAuthHeader? =
{ call -> call.request.parseAuthorizationHeaderOrNull() }
var firebaseAuthenticationFunction: AuthenticationFunction<FirebaseToken> = {
throw NotImplementedError(FirebaseImplementationError)
}
fun validate(validate: suspend ApplicationCall.(FirebaseToken) -> User?) {
firebaseAuthenticationFunction = validate
}
}
public fun AuthenticationConfig.firebase(name: String? = FIREBASE_AUTH, configure: FirebaseConfig.() -> Unit) {
val provider = FirebaseAuthProvider(FirebaseConfig(name).apply(configure))
register(provider)
}
suspend fun verifyFirebaseIdToken(
call: ApplicationCall,
authHeader: HttpAuthHeader,
tokenData: suspend ApplicationCall.(FirebaseToken) -> Principal?
): Principal? {
val token: FirebaseToken = try {
if (authHeader.authScheme == "Bearer" && authHeader is HttpAuthHeader.Single) {
withContext(Dispatchers.IO) {
FirebaseAuth.getInstance().verifyIdToken(authHeader.blob)
}
} else {
null
}
} catch (ex: Exception) {
ex.printStackTrace()
return null
} ?: return null
return tokenData(call, token)
}
private fun HttpAuthHeader.Companion.bearerAuthChallenge(realm: String): HttpAuthHeader {
return HttpAuthHeader.Parameterized("Bearer", mapOf(HttpAuthHeader.Parameters.Realm to realm))
}
private fun ApplicationRequest.parseAuthorizationHeaderOrNull() = try {
parseAuthorizationHeader()
} catch (ex: IllegalArgumentException) {
println("failed to parse token")
null
}
const val FIREBASE_AUTH = "FIREBASE_AUTH"
private const val FirebaseJWTAuthKey: String = "FirebaseAuth"
private const val FirebaseImplementationError =
"Firebase auth validate function is not specified, use firebase { validate { ... } }to fix"
Then to actually use in your project created an extension function on Application. Be sure that the Firebase Admin SDK has been initialized with credentials before installing the Firebase authentication plugin on Ktor.
fun Application.configureFirebaseAuth() {
FirebaseAdmin.init()
install(Authentication) {
firebase {
validate {
// TODO look up user profile to fill in any additional information on top of firebase user profile
User(it.uid, it.name)
}
}
}
}
Finally wrap a route with the authentication function:
authenticate(FIREBASE_AUTH) {
get("/authenticated") {
val user: User = call.principal() ?: return#get call.respond(HttpStatusCode.Unauthorized)
call.respond("User is authenticated: $user")
}
}
The converted to Ktor 2.0.* code is the following:
import io.ktor.http.auth.*
import io.ktor.serialization.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseToken
class FirebaseAuthProvider(config: FirebaseConfig): AuthenticationProvider(config) {
val authHeader: (ApplicationCall) -> HttpAuthHeader? = config.authHeader
private val authFunction = config.firebaseAuthenticationFunction
override suspend fun onAuthenticate(context: AuthenticationContext) {
val token = authHeader(context.call)
if (token == null) {
context.challenge(FirebaseJWTAuthKey, AuthenticationFailedCause.InvalidCredentials) { challengeFunc, call ->
challengeFunc.complete()
call.respond(UnauthorizedResponse(HttpAuthHeader.bearerAuthChallenge(realm = "firebaseAuth")))
}
return
}
try {
val principal = verifyFirebaseIdToken(context.call, token, authFunction)
if (principal != null) {
context.principal(principal)
}
} catch (cause: Throwable) {
val message = cause.message ?: cause.javaClass.simpleName
context.error(FirebaseJWTAuthKey, AuthenticationFailedCause.Error(message))
}
}
}
class FirebaseConfig(name: String?) : AuthenticationProvider.Config(name) {
internal var authHeader: (ApplicationCall) -> HttpAuthHeader? =
{ call -> call.request.parseAuthorizationHeaderOrNull() }
var firebaseAuthenticationFunction: AuthenticationFunction<FirebaseToken> = {
throw NotImplementedError(FirebaseImplementationError)
}
fun validate(validate: suspend ApplicationCall.(FirebaseToken) -> User?) {
firebaseAuthenticationFunction = validate
}
}
public fun AuthenticationConfig.firebase(name: String? = "firebaseAuth", configure: FirebaseConfig.() -> Unit) {
val provider = FirebaseAuthProvider(FirebaseConfig(name).apply(configure))
register(provider)
}
suspend fun verifyFirebaseIdToken(
call: ApplicationCall,
authHeader: HttpAuthHeader,
tokenData: suspend ApplicationCall.(FirebaseToken) -> Principal?
): Principal? {
val token: FirebaseToken = try {
if (authHeader.authScheme == "Bearer" && authHeader is HttpAuthHeader.Single) {
withContext(Dispatchers.IO) {
FirebaseAuth.getInstance().verifyIdToken(authHeader.blob)
}
} else {
null
}
} catch (ex: Exception) {
ex.printStackTrace()
return null
} ?: return null
return tokenData(call, token)
}
private fun HttpAuthHeader.Companion.bearerAuthChallenge(realm: String): HttpAuthHeader {
return HttpAuthHeader.Parameterized("Bearer", mapOf(HttpAuthHeader.Parameters.Realm to realm))
}
private fun ApplicationRequest.parseAuthorizationHeaderOrNull() = try {
parseAuthorizationHeader()
} catch (ex: IllegalArgumentException) {
println("failed to parse token")
null
}
private const val FirebaseJWTAuthKey: String = "FirebaseAuth"
private const val FirebaseImplementationError =
"Firebase auth validate function is not specified, use firebase { { ... } }to fix"

How to restrict route access in ktor framework?

How to restrict route access in ktor framework?
//only admin
post("/add") {
call.respondText { "add" }
}
post("/delete") {
call.respondText { "delete" }
}
You can write a method that creates a route that restricts access for admins only. Inside that method, the newly created route is intercepted to inject the code for validation. In the following example, if the header admin has the value 1 then a request is made from an admin otherwise for the /add and /delete routes the response with the 401 Unauthorized status will be returned.
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.http.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.util.pipeline.*
fun main() {
embeddedServer(Netty, port = 5555, host = "0.0.0.0") {
routing {
admin {
post("/add") {
call.respondText { "add" }
}
post("/delete") {
call.respondText { "delete" }
}
}
post("/show") {
call.respondText { "show" }
}
}
}.start(wait = false)
}
private val validationPhase = PipelinePhase("Validate")
fun Route.admin(build: Route.() -> Unit): Route {
val route = createChild(AdminSelector())
route.insertPhaseAfter(ApplicationCallPipeline.Features, Authentication.ChallengePhase)
route.insertPhaseAfter(Authentication.ChallengePhase, validationPhase)
route.intercept(validationPhase) {
if (!isAdmin(call.request)) {
call.respond(HttpStatusCode.Forbidden)
finish()
}
}
route.build()
return route
}
class AdminSelector: RouteSelector() {
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int) = RouteSelectorEvaluation.Transparent
}
fun isAdmin(request: ApplicationRequest): Boolean {
return request.headers["admin"] == "1"
}
For Ktor 2.x the solution proposed by Alexsei does not work anymore because ChallengePhase is now marked as internal as they completely restructured the plugin system.
This code snippet seems to be working for me.
fun Route.authorization(build: Route.() -> Unit): Route {
val route = createChild(CustomSelector())
val plugin = createRouteScopedPlugin("CustomAuthorization") {
on(AuthenticationChecked) { call ->
val principal = call.authentication.principal
// custom logic
}
}
route.install(plugin)
route.build()
return route
}
private class CustomSelector : RouteSelector() {
override fun evaluate(context: RoutingResolveContext, segmentIndex: Int) = RouteSelectorEvaluation.Transparent
}
Of course, you can add parameters to the function specifying the restriction like required roles.
To secure a route...
fun Route.myRoute() = route("test") {
authorization {
get { ... }
}
}
For more details: https://ktor.io/docs/custom-plugins.html

Ktor: Mock Principal

I have following route:
get("/user") {
val principal: UserIdPrincipal = call.principal()
?: return#get call.respond(HttpStatusCode.Unauthorized)
val user = userService.findUserForId(principal.name.toLong())
?: return#get call.respond(HttpStatusCode.Unauthorized)
val userResponse = UserResponse(username = user.username)
call.respond(userResponse)
}
I don't want to test if authentication works for every single of my routes, so I would like to mock call.principal(). Since it is an inline extension function, it cannot be easily mocked. Any ideas how to solve this problem?
If you can change the configuration for the Authentication plugin in a test then you can register a provider, intercept its pipeline in the AuthenticationPipeline.RequestAuthentication phase, to get an access to authentication context, and finally assign new principal. Here is an example:
import io.ktor.application.*
import io.ktor.auth.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
fun main() {
val server = embeddedServer(Netty, port = 8080) {
install(Authentication) {
provider {
pipeline.intercept(AuthenticationPipeline.RequestAuthentication) { context ->
context.principal(UserIdPrincipal("principal"))
}
}
}
routing {
authenticate {
get("/user") {
val principal: UserIdPrincipal = call.principal()!!
call.respond(principal.name)
}
}
}
}
server.start(wait = true)
}

How to implement volleyresponse listener using Kotlin

I am trying to move my volley requests into a class, so I can use it for multiple network calls. I need a way to access the response listener in whatever activity I use in this class. i saw some examples in java, But I am finding it difficult to do achieve this.
import android.content.Context
import com.android.volley.DefaultRetryPolicy
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
interface VolleyResponse{
}
class NetworkCall(LINK:String,
CONTEXT:Context,
CACHE:Boolean,
PARAMS: HashMap<String, String> = HashMap(),
SuccessListener: Response.Listener<String>,
ErrorListener: Response.ErrorListener ) {
private var link:String = LINK
private var context: Context = CONTEXT
var cache: Boolean = CACHE
var PARAMS: HashMap<String,String> = HashMap()
fun RunTask( ){
//BUILD the request and listen for error or success
var request = object : StringRequest(
Request.Method.POST,link,
Response.Listener { response -> { }
},
Response.ErrorListener { error -> { }
}) {
override fun getParams(): HashMap<String, String> {
return PARAMS
}
}
var RequestQueue: RequestQueue = Volley.newRequestQueue(context)
request.setShouldCache(cache)
request.setRetryPolicy(DefaultRetryPolicy(10000, 0, 0F))
}
}
and i call it like this...
fun processLogin() {
var params:HashMap<String,String> = HashMap()
params.put("user_email","username")
params.put("user_password","password")
var networkCall = NetworkCall("",applicationContext,false,params)
}
I just need to be able to access the response listeners in my processLogin function.
First you have to define implementations of Response.Listener<String> and Response.ErrorListener in the class where processLogin is defined, this can be done as following
private val successListener = Response.Listener<String> {
// Do something when response is received
}
private val errorListener = Response.ErrorListener {
// Do something when error is received
}
now pass these as parameters when you call processLogin as following
var networkCall = NetworkCall("",applicationContext,false,params, successListener, errorListener)
Finally you need to update your NetworkCall class so that these listeners are called on network action
fun RunTask( ){
//BUILD the request and listen for error or success
var request = object : StringRequest(
Request.Method.POST,link,
SuccessListener, // Pass listeners to request
ErrorListener) {
override fun getParams(): HashMap<String, String> {
return PARAMS
}
}

Ktor HttpClient Mock Fails

I'm trying to create a Ktor (1.3.1) HttpClient Mock with JsonFeature like this:
#Test
fun mockFailure() = runBlocking {
val mock = MockEngine { call ->
respond("{}",
HttpStatusCode.OK,
headersOf("Content-Type", ContentType.Application.Json.toString()))
}
val client = HttpClient(mock) {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
}
val resp = client.get<JsonObject>("dsf")
}
It appears to process it correctly, but then I get this error:
io.ktor.client.call.NoTransformationFoundException: No transformation found: class kotlinx.coroutines.io.ByteBufferChannel -> class kotlinx.serialization.json.JsonObject
with response from http://localhost/dsf:
status: 200 OK
response headers:
Content-Type: application/json
at io.ktor.client.call.HttpClientCall.receive(HttpClientCall.kt:79)
Try installing client content negotiation plugin in the HttpClient block:
val client = HttpClient(mock) {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
//Here
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
})
}
}
Required dependencies implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
Json Serialization implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
Also change the last line to val resp = client.get<JsonObject>("dsf").body()