Skip to content
Open
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
34 changes: 24 additions & 10 deletions core/src/main/java/com/adobe/aio/feign/AIOHeaderInterceptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@

public class AIOHeaderInterceptor implements RequestInterceptor {

private final Workspace workspace;
private final String apiKey;
private final String imsOrgId;

private AIOHeaderInterceptor(final Workspace workspace) {
this.workspace = workspace;
private AIOHeaderInterceptor(final String apiKey, final String imsOrgId) {
this.apiKey = apiKey;
this.imsOrgId = imsOrgId;
}

@Override
Expand All @@ -34,14 +36,14 @@ public void apply(RequestTemplate requestTemplate) {


private void applyApiKey(RequestTemplate requestTemplate) {
if (!requestTemplate.headers().containsKey(API_KEY_HEADER) && !StringUtils.isEmpty(workspace.getApiKey())) {
requestTemplate.header(API_KEY_HEADER, workspace.getApiKey());
if (!requestTemplate.headers().containsKey(API_KEY_HEADER) && !StringUtils.isEmpty(apiKey)) {
requestTemplate.header(API_KEY_HEADER, apiKey);
}
}

private void applyImsOrg(RequestTemplate requestTemplate) {
if (!requestTemplate.headers().containsKey(IMS_ORG_HEADER) && !StringUtils.isEmpty(workspace.getImsOrgId())) {
requestTemplate.header(IMS_ORG_HEADER, workspace.getImsOrgId());
if (!requestTemplate.headers().containsKey(IMS_ORG_HEADER) && !StringUtils.isEmpty(imsOrgId)) {
requestTemplate.header(IMS_ORG_HEADER, imsOrgId);
}
}

Expand All @@ -51,18 +53,30 @@ public static Builder builder() {

public static class Builder {

private Workspace workspace;;
private String apiKey;
private String imsOrgId;

private Builder() {
}

public Builder workspace(Workspace workspace) {
this.workspace = workspace;
this.apiKey = workspace.getApiKey();
this.imsOrgId = workspace.getImsOrgId();
return this;
}

public Builder apiKey(String apiKey) {
this.apiKey = apiKey;
return this;
}

public Builder imsOrgId(String imsOrgId) {
this.imsOrgId = imsOrgId;
return this;
}

public AIOHeaderInterceptor build() {
return new AIOHeaderInterceptor(workspace);
return new AIOHeaderInterceptor(apiKey, imsOrgId);
}
}

Expand Down
26 changes: 13 additions & 13 deletions core/src/main/java/com/adobe/aio/workspace/Workspace.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,29 @@ public static Builder builder() {
return new Builder();
}

/**
* Validates that this workspace is properly defined, including its `auth` (credential and api-key) related properties.
* @throws IllegalStateException if any workspace `context` or `auth` properties is missing.
* @see #validateWorkspaceContext() where validation that does not include `auth` related properties.
*/
public void validateAll() {
validateWorkspaceContext();
if (StringUtils.isEmpty(apiKey)) {
throw new IllegalStateException("Your `Workspace` is missing an apiKey");
}
if (!isAuthOAuth() && !isAuthJWT()) {
throw new IllegalStateException("Missing auth configuration, set either jwt or oauth...");
}
authContext.validate();
}

/**
* Validates that this workspace context is populated.
* Validates that this workspace is properly defined.
*
* @throws IllegalStateException if any properties are not specified.
* @throws IllegalStateException if a context/basic properties is missing.
* Note that `auth` related properties will not be validated here.
* @see #validateAll() for a more comprehensive validation that includes `auth` (credential and api-key) related properties.
*
*/
public void validateWorkspaceContext() throws IllegalStateException {
if (StringUtils.isEmpty(imsOrgId)) {
Expand All @@ -76,23 +87,12 @@ public void validateWorkspaceContext() throws IllegalStateException {
if (StringUtils.isEmpty(this.getConsumerOrgId())) {
throw new IllegalStateException("Your `Workspace` is missing a consumerOrgId");
}
if (StringUtils.isEmpty(apiKey)) {
throw new IllegalStateException("Your `Workspace` is missing an apiKey");
}
if (StringUtils.isEmpty(this.getProjectId())) {
throw new IllegalStateException("Your `Workspace` is missing a projectId");
}
if (StringUtils.isEmpty(this.getWorkspaceId())) {
throw new IllegalStateException("Your `Workspace` is missing a workspaceId");
}
// note that the credentialId is optional
// but it might be handy to have it in your `Workspace` POJO,
// to avoid confusion when you have multiple credentials,
// and to eventually in some Adobe API calls

if (authContext == null) {
throw new IllegalStateException("Missing auth configuration ...");
}
}

public String getProjectUrl() {
Expand Down
40 changes: 21 additions & 19 deletions core/src/test/java/com/adobe/aio/workspace/WorkspaceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.util.HashSet;
import java.util.Set;

import com.adobe.aio.auth.Context;
import com.adobe.aio.auth.OAuthContext;
import com.adobe.aio.util.Constants;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
Expand All @@ -38,22 +41,16 @@ public static void beforeClass() throws Exception {

@Test
public void successFullBuilder() throws IOException {

class MockContext implements Context {
@Override
public void validate() {

}
}

Set<String> scopes = new HashSet<>();
scopes.add("scope1");
Workspace actual = Workspace.builder()
.imsUrl(Constants.PROD_IMS_URL)
.imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE)
.apiKey(Workspace.API_KEY + TEST_VALUE)
.consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE)
.projectId(Workspace.PROJECT_ID + TEST_VALUE)
.workspaceId(Workspace.WORKSPACE_ID + TEST_VALUE)
.authContext(new MockContext())
.authContext(new OAuthContext("someClientSecret", scopes))
.build();

assertEquals(Workspace.IMS_ORG_ID + TEST_VALUE, actual.getImsOrgId());
Expand All @@ -63,6 +60,21 @@ public void validate() {
assertEquals(Workspace.WORKSPACE_ID + TEST_VALUE, actual.getWorkspaceId());
assertEquals(Constants.PROD_IMS_URL, actual.getImsUrl());
actual.validateWorkspaceContext();
actual.validateAll();
}

@Test
public void missingApiKey() {
Workspace actual = Workspace.builder()
.imsUrl(Constants.PROD_IMS_URL)
.imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE)
.consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE)
.projectId(Workspace.PROJECT_ID + TEST_VALUE)
.workspaceId(Workspace.WORKSPACE_ID + TEST_VALUE)
.build();
actual.validateWorkspaceContext();
Exception ex = assertThrows(IllegalStateException.class, actual::validateAll);
assertEquals("Your `Workspace` is missing an apiKey", ex.getMessage());
}

@Test
Expand All @@ -81,16 +93,6 @@ public void missingConsumerOrgId() {
assertEquals("Your `Workspace` is missing a consumerOrgId", ex.getMessage());
}

@Test
public void missingApiKey() {
Workspace actual = Workspace.builder()
.imsOrgId(Workspace.IMS_ORG_ID + TEST_VALUE)
.consumerOrgId(Workspace.CONSUMER_ORG_ID + TEST_VALUE)
.build();
Exception ex = assertThrows(IllegalStateException.class, actual::validateWorkspaceContext);
assertEquals("Your `Workspace` is missing an apiKey", ex.getMessage());
}

@Test
public void missingProjectId() {
Workspace actual = Workspace.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

import com.adobe.aio.event.management.feign.FeignProviderService;
import com.adobe.aio.event.management.model.SampleEvent;
import com.adobe.aio.feign.AIOHeaderInterceptor;
import com.adobe.aio.ims.feign.AuthInterceptor;
import com.adobe.aio.workspace.Workspace;
import com.adobe.aio.event.management.model.EventMetadata;
import com.adobe.aio.event.management.model.Provider;
Expand Down Expand Up @@ -92,6 +94,8 @@ Optional<SampleEvent> getSampleEvent(String providerId,
class Builder {

private Workspace workspace;
private AuthInterceptor authInterceptor;
private AIOHeaderInterceptor aioHeaderInterceptor;
private String url;

public Builder() {
Expand All @@ -102,13 +106,29 @@ public Builder workspace(Workspace workspace) {
return this;
}

public Builder authInterceptor(AuthInterceptor authInterceptor) {
this.authInterceptor = authInterceptor;
return this;
}

public Builder aioHeaderInterceptor(AIOHeaderInterceptor aioHeaderInterceptor) {
this.aioHeaderInterceptor = aioHeaderInterceptor;
return this;
}

public Builder url(String url) {
this.url = url;
return this;
}

public ProviderService build() {
return new FeignProviderService(workspace, url);
if (authInterceptor == null) {
authInterceptor = AuthInterceptor.builder().workspace(workspace).build();
}
if (aioHeaderInterceptor == null) {
aioHeaderInterceptor = AIOHeaderInterceptor.builder().workspace(workspace).build();
}
return new FeignProviderService(authInterceptor, aioHeaderInterceptor, workspace, url);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,12 @@ public class FeignProviderService implements ProviderService {
private final SampleEventApi sampleEventApi;
private final Workspace workspace;

public FeignProviderService(final Workspace workspace, final String url) {
public FeignProviderService(final AuthInterceptor authInterceptor, final AIOHeaderInterceptor aioHeaderInterceptor, Workspace workspace, final String url) {
String apiUrl = StringUtils.isEmpty(url) ? API_MANAGEMENT_URL : url;

if (workspace == null) {
throw new IllegalArgumentException("ProviderService is missing a workspace context");
}
workspace.validateWorkspaceContext();
RequestInterceptor authInterceptor = AuthInterceptor.builder().workspace(workspace).build();
RequestInterceptor aioHeaderInterceptor = AIOHeaderInterceptor.builder().workspace(workspace).build();

this.providerApi = FeignUtil.getDefaultBuilder()
.requestInterceptor(authInterceptor)
Expand Down
20 changes: 20 additions & 0 deletions ims/src/main/java/com/adobe/aio/ims/AccessTokenProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2017 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package com.adobe.aio.ims;

import com.adobe.aio.ims.model.AccessToken;

@FunctionalInterface
public interface AccessTokenProvider {

AccessToken getAccessToken();
}
6 changes: 3 additions & 3 deletions ims/src/main/java/com/adobe/aio/ims/ImsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ public Builder workspace(Workspace workspace) {
* Builds an IMS Service instance.
*
* @return a configured IMS Service
* @throws IllegalStateException if the Workspace authentication context is not valid.
* @throws IllegalArgumentException if the Workspace authentication context is not valid.
*/
public ImsService build() throws IllegalStateException {
public ImsService build() throws IllegalArgumentException {
if (workspace == null) {
throw new IllegalStateException("Workspace is required to build ImsService");
throw new IllegalArgumentException("Workspace is required to build ImsService");
}
workspace.validateAll();
return new FeignImsService(this.workspace);
Expand Down
17 changes: 9 additions & 8 deletions ims/src/main/java/com/adobe/aio/ims/feign/AuthInterceptor.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.adobe.aio.ims.feign;

import com.adobe.aio.ims.AccessTokenProvider;
import com.adobe.aio.ims.ImsService;
import com.adobe.aio.ims.model.AccessToken;
import com.adobe.aio.workspace.Workspace;
Expand All @@ -12,23 +13,23 @@ public class AuthInterceptor implements RequestInterceptor {

private volatile Long expirationTimeMillis;
private volatile AccessToken accessToken;
private final ImsService imsService;
private final AccessTokenProvider accessTokenProvider;

protected AuthInterceptor (final Workspace workspace) {
this.imsService = ImsService.builder().workspace(workspace).build();
protected AuthInterceptor(final Workspace workspace) {
this(ImsService.builder().workspace(workspace).build()::getAccessToken);
}

public AuthInterceptor(final AccessTokenProvider accessTokenProvider) {
this.accessTokenProvider = accessTokenProvider;
}

@Override
public void apply(RequestTemplate requestTemplate) {
applyAuthorization(requestTemplate);
}

ImsService getImsService() {
return this.imsService;
}

AccessToken fetchAccessToken() {
return getImsService().getAccessToken();
return accessTokenProvider.getAccessToken();
}

synchronized String getAccessToken() {
Expand Down
16 changes: 11 additions & 5 deletions ims/src/main/java/com/adobe/aio/util/WorkspaceUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,25 @@ public static Workspace.Builder getWorkspaceBuilder(Map<String, String> configMa
.projectId(configMap.get(PROJECT_ID))
.workspaceId(configMap.get(WORKSPACE_ID))
.credentialId(configMap.get(CREDENTIAL_ID));
builder.authContext(getAuthContext(configMap));
getAuthContext(configMap).ifPresent(builder::authContext);
return builder;
}

public static boolean isOAuthConfig(Map<String, String> configMap) {
return configMap.containsKey(SCOPES);
}

public static Context getAuthContext(Map<String, String> configMap) {
public static boolean isJwtConfig(Map<String, String> configMap) {
return configMap.containsKey(META_SCOPES);
}

public static Optional<Context> getAuthContext(Map<String, String> configMap) {
if (isOAuthConfig(configMap)) {
return getOAuthContextBuilder(configMap).build();
} else {
return getJwtContextBuilder(configMap).build();
return Optional.of(getOAuthContextBuilder(configMap).build());
} else if (isJwtConfig(configMap)) {
return Optional.of(getJwtContextBuilder(configMap).build());
} else {
return Optional.empty();
}
}

Expand Down
Loading