Skip to content

Commit

Permalink
refactor: Dissociate schemes keywords used by OAS file from auth shem…
Browse files Browse the repository at this point in the history
…es names supported by HTTP. Genrator tool do not generate mapping details, so SinchClient is in charge to define them
  • Loading branch information
JPPortier committed Nov 21, 2023
1 parent 20ce178 commit cdaaf97
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 31 deletions.
13 changes: 11 additions & 2 deletions client/src/main/com/sinch/sdk/SinchClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ public class SinchClient {

private HttpClientApache httpClient;

// TODO: generator tools do not generate security schemas related information.
// Will have to fix it with a generic way
public static final String SECURITY_SCHEME_KEYWORD_NUMBERS = "BasicAuth";
public static final String SECURITY_SCHEME_KEYWORD_VERIFICATION = "Basic";
public static final String SECURITY_SCHEME_KEYWORD_SMS = "BearerAuth";

/**
* Create a Sinch Client instance based onto configuration
*
Expand Down Expand Up @@ -147,8 +153,11 @@ private HttpClientApache getHttpClient() {
BearerAuthManager bearerAuthManager = new BearerAuthManager(configuration, new HttpMapper());

Map<String, AuthManager> authManagers =
Stream.of(basicAuthManager, bearerAuthManager)
.map(e -> new AbstractMap.SimpleEntry<>(e.getSchema(), e))
Stream.of(
new AbstractMap.SimpleEntry<>(SECURITY_SCHEME_KEYWORD_NUMBERS, basicAuthManager),
new AbstractMap.SimpleEntry<>(
SECURITY_SCHEME_KEYWORD_VERIFICATION, basicAuthManager),
new AbstractMap.SimpleEntry<>(SECURITY_SCHEME_KEYWORD_SMS, bearerAuthManager))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

// TODO: by adding a setter, we could imagine having another HTTP client provided
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
import java.util.Base64;

public class BasicAuthManager implements AuthManager {
public static final String BASIC_SCHEMA_KEYWORD = "BasicAuth";
private static final String BASIC_AUTH_KEYWORD = "Basic";
private static final String SCHEMA_KEYWORD = "Basic";
private static final String AUTH_KEYWORD = "Basic";
private final Configuration configuration;

public BasicAuthManager(Configuration configuration) {
this.configuration = configuration;
}

public String getSchema() {
return BASIC_SCHEMA_KEYWORD;
return SCHEMA_KEYWORD;
}

@Override
Expand All @@ -36,7 +36,7 @@ public String getAuthorizationHeaderValue() {

String raw = key + ":" + secret;

return BASIC_AUTH_KEYWORD
return AUTH_KEYWORD
+ " "
+ Base64.getEncoder().encodeToString(raw.getBytes(StandardCharsets.UTF_8));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
import java.util.logging.Logger;

public class BearerAuthManager implements AuthManager {
public static final String BEARER_SCHEMA_KEYWORD = "BearerAuth";
public static final String BEARER_EXPIRED_KEYWORD = "expired";
public static final String BEARER_AUTHENTICATE_RESPONSE_HEADER_KEYWORD = "www-authenticate";
private static final Logger LOGGER = Logger.getLogger(BearerAuthManager.class.getName());
private static final String BEARER_AUTH_KEYWORD = "Bearer";
private static final String SCHEMA_KEYWORD = "Bearer";
private static final String AUTH_KEYWORD = "Bearer";
private static final int maxRefreshAttempt = 5;
private final Configuration configuration;
private final HttpMapper mapper;
Expand All @@ -32,7 +32,7 @@ public BearerAuthManager(Configuration configuration, HttpMapper mapper) {
}

public String getSchema() {
return BEARER_SCHEMA_KEYWORD;
return SCHEMA_KEYWORD;
}

@Override
Expand All @@ -51,7 +51,7 @@ public String getAuthorizationHeaderValue() {
if (token == null) {
refreshToken();
}
return BEARER_AUTH_KEYWORD + " " + token;
return AUTH_KEYWORD + " " + token;
}

private void refreshToken() {
Expand Down Expand Up @@ -80,7 +80,7 @@ private Optional<String> getNewToken() {
null,
null,
Collections.singletonList("application/x-www-form-urlencoded"),
Collections.singletonList(BasicAuthManager.BASIC_SCHEMA_KEYWORD));
Collections.singletonList("Basic"));
try {
HttpResponse httpResponse = httpClient.invokeAPI(configuration.getOAuthServer(), request);
BearerAuthResponse authResponse =
Expand Down
64 changes: 51 additions & 13 deletions client/src/main/com/sinch/sdk/http/HttpClientApache.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@
import static com.sinch.sdk.core.http.URLParameterUtils.encodeParametersAsString;

import com.sinch.sdk.auth.AuthManager;
import com.sinch.sdk.auth.adapters.BearerAuthManager;
import com.sinch.sdk.core.exceptions.ApiException;
import com.sinch.sdk.core.http.*;
import com.sinch.sdk.core.http.HttpMethod;
import com.sinch.sdk.core.http.HttpRequest;
import com.sinch.sdk.core.http.HttpResponse;
import com.sinch.sdk.core.http.HttpStatus;
import com.sinch.sdk.core.http.URLParameter;
import com.sinch.sdk.core.models.ServerConfiguration;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Scanner;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ClassicHttpRequest;
Expand All @@ -23,14 +34,28 @@
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;

public class HttpClientApache implements com.sinch.sdk.core.http.HttpClient {

private static final Logger LOGGER = Logger.getLogger(HttpClientApache.class.getName());
private static final String AUTHORIZATION_HEADER_KEYWORD = "Authorization";
private final Map<String, AuthManager> authManagers;
/** Schemes names (could be aliases) from OAS files */
private final Map<String, AuthManager> authManagersByOasSecuritySchemes;
/**
* HTTP Authentication schemes: <a
* href="https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml">...</a>
*/
private final Map<String, AuthManager> authManagersByAuthSchemes;

private static final String BEARER_SCHEME_NAME = "Bearer";

private CloseableHttpClient client;

public HttpClientApache(Map<String, AuthManager> authManagers) {
public HttpClientApache(Map<String, AuthManager> authManagersByOasSecuritySchemes) {
this.client = HttpClients.createDefault();
this.authManagers = authManagers;
this.authManagersByOasSecuritySchemes = authManagersByOasSecuritySchemes;
this.authManagersByAuthSchemes =
authManagersByOasSecuritySchemes.values().stream()
.map(authManager -> new AbstractMap.SimpleEntry<>(authManager.getSchema(), authManager))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a1, a2) -> a1));
}

private static HttpResponse processResponse(ClassicHttpResponse response) throws IOException {
Expand Down Expand Up @@ -125,12 +150,25 @@ public HttpResponse invokeAPI(ServerConfiguration serverConfiguration, HttpReque

private boolean processUnauthorizedResponse(HttpRequest request, HttpResponse response) {

AuthManager bearerAuthManager = authManagers.get(BearerAuthManager.BEARER_SCHEMA_KEYWORD);
// is request was with Bearer authentication ?
Collection<String> auths = request.getAuthNames();
AuthManager bearerAuthManager = authManagersByAuthSchemes.get(BEARER_SCHEME_NAME);
if (null == bearerAuthManager) {
// no bearer manager registered
return false;
}

if (null == bearerAuthManager || !auths.contains(BearerAuthManager.BEARER_SCHEMA_KEYWORD)) {
// not required to ask for a new token: original request is not using it
Collection<String> auths = request.getAuthNames();
Optional<String> requestSupportBearerAuthentication =
auths.stream()
.filter(
f ->
null != authManagersByOasSecuritySchemes.get(f)
&& authManagersByOasSecuritySchemes
.get(f)
.getSchema()
.equals(BEARER_SCHEME_NAME))
.findFirst();

if (!requestSupportBearerAuthentication.isPresent()) {
return false;
}
// looking for "expired" keyword present in "www-authenticate" header
Expand Down Expand Up @@ -175,8 +213,8 @@ private void addAuth(ClassicRequestBuilder requestBuilder, Collection<String> va
}

for (String entry : values) {
if (authManagers.containsKey(entry)) {
AuthManager authManager = authManagers.get(entry);
if (authManagersByOasSecuritySchemes.containsKey(entry)) {
AuthManager authManager = authManagersByOasSecuritySchemes.get(entry);
requestBuilder.setHeader(
AUTHORIZATION_HEADER_KEYWORD, authManager.getAuthorizationHeaderValue());
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class BasicAuthManagerTest {

@Test
void getSchema() {
assertEquals("BasicAuth", authManager.getSchema());
assertEquals("Basic", authManager.getSchema());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class BearerAuthManagerTest extends BaseTest {

@Test
void getSchema() {
assertEquals("BearerAuth", authManager.getSchema());
assertEquals("Bearer", authManager.getSchema());
}

@Test
Expand Down Expand Up @@ -79,7 +79,7 @@ void callToOAuthServer() {

HttpRequest httpRequestCaptorValue = httpRequestCaptor.getValue();
assertEquals(HttpMethod.POST, httpRequestCaptorValue.getMethod());
assertTrue(httpRequestCaptorValue.getAuthNames().stream().anyMatch(e -> e.equals("BasicAuth")));
assertTrue(httpRequestCaptorValue.getAuthNames().stream().anyMatch(e -> e.equals("Basic")));
assertTrue(
httpRequestCaptorValue.getContentType().stream()
.anyMatch(e -> e.equals("application/x-www-form-urlencoded")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ void basicAuthorization() throws InterruptedException {
null,
null,
null,
Collections.singletonList(BasicAuthManager.BASIC_SCHEMA_KEYWORD)));
Collections.singletonList(basicAuthManager.getSchema())));
RecordedRequest recordedRequest = mockBackEnd.takeRequest();

String header = recordedRequest.getHeader("Authorization");
Expand All @@ -107,7 +107,7 @@ void initialBearerAuthorization() throws InterruptedException {
null,
null,
null,
Collections.singletonList(BearerAuthManager.BEARER_SCHEMA_KEYWORD)));
Collections.singletonList(bearerAuthManager.getSchema())));

RecordedRequest recordedRequest = mockBackEnd.takeRequest();

Expand Down Expand Up @@ -149,7 +149,7 @@ void bearerAutoRefresh() throws InterruptedException {
null,
null,
null,
Collections.singletonList(BearerAuthManager.BEARER_SCHEMA_KEYWORD)));
Collections.singletonList(bearerAuthManager.getSchema())));
} catch (ApiException ae) {
// noop
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public GetForNumber() throws IOException {}

public static void main(String[] args) {
try {
new Get().run();
new GetForNumber().run();
} catch (Exception e) {
LOGGER.severe(e.getMessage());
e.printStackTrace();
Expand Down

0 comments on commit cdaaf97

Please sign in to comment.