Skip to content

Commit d718aab

Browse files
authored
Merge pull request #432 from Ecwid/authorization-header
use headers for auth instead of parameters
2 parents 2df1e71 + 9021ccb commit d718aab

5 files changed

Lines changed: 92 additions & 39 deletions

File tree

src/main/kotlin/com/ecwid/apiclient/v3/ApiClientHelper.kt

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ import java.util.logging.Logger
3030
import kotlin.random.Random
3131
import kotlin.reflect.KClass
3232

33-
const val API_TOKEN_PARAM_NAME = "token"
34-
private const val APP_CLIENT_ID_PARAM_NAME = "appClientId"
35-
const val APP_CLIENT_SECRET_PARAM_NAME = "appSecretKey"
33+
private const val APP_CLIENT_ID_HEADER_NAME = "X-Ecwid-App-Client-Id"
34+
private const val APP_CLIENT_SECRET_HEADER_NAME = "X-Ecwid-App-Secret-Key"
3635
private const val RESPONSE_FIELDS_PARAM_NAME = "responseFields"
3736
private const val REQUEST_ID_HEADER_NAME = "X-Ecwid-Api-Request-Id"
3837

@@ -238,7 +237,7 @@ class ApiClientHelper private constructor(
238237
return
239238
}
240239

241-
val securePatterns = createSecurePatterns(loggingSettings)
240+
val securePatterns = createSecurePatterns()
242241
logEntry(
243242
prefix = "Request",
244243
logLevel = Level.INFO,
@@ -259,10 +258,8 @@ class ApiClientHelper private constructor(
259258
@PublishedApi
260259
internal fun RequestInfo.toHttpRequest(requestId: String, responseFieldsOverride: ResponseFields?): HttpRequest {
261260
val uri = createApiEndpointUri(pathSegments)
262-
val params = params
263-
.withCredentialsParams(credentials)
264-
.let { if (responseFieldsOverride != null) it.withResponseFieldsParam(responseFieldsOverride) else it }
265-
val headers = headers.withRequestId(requestId)
261+
val params = if (responseFieldsOverride != null) params.withResponseFieldsParam(responseFieldsOverride) else params
262+
val headers = headers.withRequestId(requestId).withCredentials(credentials)
266263

267264
return when (method) {
268265
HttpMethod.GET -> HttpRequest.HttpGetRequest(
@@ -313,7 +310,7 @@ class ApiClientHelper private constructor(
313310
return
314311
}
315312

316-
val securePatterns = createSecurePatterns(loggingSettings)
313+
val securePatterns = createSecurePatterns()
317314
logEntry(
318315
prefix = "Response",
319316
logLevel = Level.INFO,
@@ -440,9 +437,9 @@ internal fun generateRequestId(): String {
440437
}
441438

442439
@PublishedApi
443-
internal fun Map<String, String>.withCredentialsParams(credentials: ApiCredentials) = when (credentials) {
444-
is ApiStoreCredentials -> this.withApiTokenParam(credentials.apiToken)
445-
is ApiAppCredentials -> withAppCredentialsParams(credentials)
440+
internal fun Map<String, String>.withCredentials(credentials: ApiCredentials) = when (credentials) {
441+
is ApiStoreCredentials -> this.withApiTokenHeader(credentials.apiToken)
442+
is ApiAppCredentials -> withAppCredentialsHeaders(credentials)
446443
}
447444

448445
internal fun Map<String, String>.withResponseFieldsParam(responseFields: ResponseFields): Map<String, String> {
@@ -454,20 +451,20 @@ internal fun Map<String, String>.withResponseFieldsParam(responseFields: Respons
454451
}
455452

456453
@PublishedApi
457-
internal fun Map<String, String>.withApiTokenParam(apiToken: String): Map<String, String> {
454+
internal fun Map<String, String>.withApiTokenHeader(apiToken: String): Map<String, String> {
458455
return toMutableMap()
459456
.apply {
460-
put(API_TOKEN_PARAM_NAME, apiToken)
457+
put("Authorization", "Bearer $apiToken")
461458
}
462459
.toMap()
463460
}
464461

465462
@PublishedApi
466-
internal fun Map<String, String>.withAppCredentialsParams(appCredentials: ApiAppCredentials): Map<String, String> {
463+
internal fun Map<String, String>.withAppCredentialsHeaders(appCredentials: ApiAppCredentials): Map<String, String> {
467464
return toMutableMap()
468465
.apply {
469-
put(APP_CLIENT_ID_PARAM_NAME, appCredentials.clientId)
470-
put(APP_CLIENT_SECRET_PARAM_NAME, appCredentials.clientSecret)
466+
put(APP_CLIENT_ID_HEADER_NAME, appCredentials.clientId)
467+
put(APP_CLIENT_SECRET_HEADER_NAME, appCredentials.clientSecret)
471468
}
472469
.toMap()
473470
}

src/main/kotlin/com/ecwid/apiclient/v3/config/LoggingSettings.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ data class LoggingSettings(
44
val logRequest: Boolean = true,
55
val logRequestParams: Boolean = true,
66
val logRequestBody: Boolean = false,
7-
/* DISABLE FOR DEBUG PURPOSES ONLY! */
8-
val maskRequestApiToken: Boolean = true,
9-
/* DISABLE FOR DEBUG PURPOSES ONLY! */
10-
val maskRequestApiSecretKey: Boolean = true,
117

128
val logResponse: Boolean = true,
139
val logSuccessfulResponseBody: Boolean = false,

src/main/kotlin/com/ecwid/apiclient/v3/httptransport/impl/ApacheCommonsHttpClientTransport.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ open class ApacheCommonsHttpClientTransport(
128128
val httpClientBuilder = HttpClientBuilder.create()
129129
.setConnectionManager(connectionManager)
130130
.setDefaultRequestConfig(requestConfig)
131+
.setRedirectStrategy(RemoveDisallowedHeadersRedirectStrategy())
131132
// TODO .setRetryHandler()
132133
// TODO .setServiceUnavailableRetryStrategy()
133134
if (defaultHeaders.isNotEmpty()) {
@@ -137,3 +138,4 @@ open class ApacheCommonsHttpClientTransport(
137138
}
138139
}
139140
}
141+
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.ecwid.apiclient.v3.httptransport.impl
2+
3+
import org.apache.http.Header
4+
import org.apache.http.HttpStatus
5+
import org.apache.http.client.methods.HttpGet
6+
import org.apache.http.client.methods.HttpHead
7+
import org.apache.http.client.methods.HttpUriRequest
8+
import org.apache.http.client.methods.RequestBuilder
9+
import org.apache.http.impl.client.DefaultRedirectStrategy
10+
import org.apache.http.protocol.HttpContext
11+
12+
/**
13+
* List of headers that can be passed to a redirect location.
14+
* We should NOT expose any custom headers and Authorization header to external sources
15+
*/
16+
private val allowedHeaders = setOf(
17+
"Accept",
18+
"Accept-Charset",
19+
"Accept-Encoding",
20+
"Accept-Language",
21+
"Access-Control-Request-Method",
22+
"Access-Control-Request-Headers",
23+
"Cache-Control",
24+
"Connection",
25+
"Content-Encoding",
26+
"Content-Length",
27+
"Content-Type",
28+
"Date",
29+
"Host",
30+
"HTTP2-Settings",
31+
"If-Match",
32+
"If-Modified-Since",
33+
"If-None-Match",
34+
"If-Unmodified-Since",
35+
"Origin",
36+
"Referer",
37+
"User-Agent",
38+
"X-Forwarded-For",
39+
"X-Forwarded-Host",
40+
"X-Forwarded-Proto"
41+
)
42+
43+
class RemoveDisallowedHeadersRedirectStrategy : DefaultRedirectStrategy() {
44+
override fun getRedirect(
45+
request: org.apache.http.HttpRequest,
46+
response: org.apache.http.HttpResponse,
47+
context: HttpContext?
48+
): HttpUriRequest {
49+
val uri = getLocationURI(request, response, context)
50+
val method = request.requestLine.method
51+
return if (method.equals(HttpHead.METHOD_NAME, ignoreCase = true)) {
52+
object : HttpHead(uri) {
53+
override fun setHeaders(headers: Array<out Header>) {
54+
super.setHeaders(headers.filter { it.name in allowedHeaders }.toTypedArray())
55+
}
56+
}
57+
} else {
58+
val httpGet = object : HttpGet(uri) {
59+
override fun setHeaders(headers: Array<out Header>) {
60+
super.setHeaders(headers.filter { it.name in allowedHeaders }.toTypedArray())
61+
}
62+
}
63+
if (method.equals(HttpGet.METHOD_NAME, ignoreCase = true)) {
64+
httpGet
65+
} else {
66+
val status = response.statusLine.statusCode
67+
if (status == HttpStatus.SC_TEMPORARY_REDIRECT || status == SC_PERMANENT_REDIRECT) {
68+
RequestBuilder.copy(request).setUri(uri).build()
69+
} else {
70+
httpGet
71+
}
72+
}
73+
}
74+
}
75+
}
Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
package com.ecwid.apiclient.v3.util
22

3-
import com.ecwid.apiclient.v3.API_TOKEN_PARAM_NAME
4-
import com.ecwid.apiclient.v3.APP_CLIENT_SECRET_PARAM_NAME
5-
import com.ecwid.apiclient.v3.config.LoggingSettings
6-
73
private const val PARAM_VALUE_PATTERN = "([^;,)]+)"
84

9-
private val API_TOKEN_SECURE_PATTERN = SecurePattern(
10-
regex = Regex("$API_TOKEN_PARAM_NAME=(?:secret_|public_|)$PARAM_VALUE_PATTERN"),
11-
unmaskedLength = 6
12-
)
13-
14-
private val API_SECRET_KEY_SECURE_PATTERN = createKeyValueSecurePattern(APP_CLIENT_SECRET_PARAM_NAME)
15-
165
private val GLOBAL_SECURE_PATTERNS = listOf(
176
createKeyValueSecurePattern("email"),
187
createJsonSecurePattern("email"),
@@ -35,12 +24,6 @@ fun createJsonSecurePattern(paramName: String) = SecurePattern(
3524
unmaskedLength = 6
3625
)
3726

38-
fun createSecurePatterns(loggingSettings: LoggingSettings) = mutableListOf<SecurePattern>().apply {
39-
if (loggingSettings.maskRequestApiToken) {
40-
add(API_TOKEN_SECURE_PATTERN)
41-
}
42-
if (loggingSettings.maskRequestApiSecretKey) {
43-
add(API_SECRET_KEY_SECURE_PATTERN)
44-
}
27+
fun createSecurePatterns() = mutableListOf<SecurePattern>().apply {
4528
addAll(GLOBAL_SECURE_PATTERNS)
4629
}.toList()

0 commit comments

Comments
 (0)