/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.module.extension.internal.runtime.connectivity.oauth; import static java.lang.String.format; import static java.util.Collections.unmodifiableMap; import static java.util.stream.Collectors.toList; import static org.mule.runtime.module.extension.internal.runtime.connectivity.oauth.ExtensionsOAuthUtils.toAuthorizationCodeState; import static org.mule.runtime.module.extension.internal.util.IntrospectionUtils.getFields; import org.mule.runtime.api.connection.ConnectionException; import org.mule.runtime.api.connection.ConnectionProvider; import org.mule.runtime.api.exception.MuleException; import org.mule.runtime.core.api.retry.RetryPolicyTemplate; import org.mule.runtime.core.internal.connection.ReconnectableConnectionProviderWrapper; import org.mule.runtime.core.util.func.Once; import org.mule.runtime.extension.api.annotation.connectivity.oauth.OAuthCallbackValue; import org.mule.runtime.extension.api.connectivity.oauth.AuthorizationCodeState; import org.mule.runtime.extension.api.exception.IllegalConnectionProviderModelDefinitionException; import org.mule.runtime.module.extension.internal.runtime.connectivity.NoConnectivityTest; import org.mule.runtime.module.extension.internal.util.FieldSetter; import org.mule.runtime.oauth.api.state.ResourceOwnerOAuthContext; import java.lang.reflect.Field; import java.util.List; import java.util.Map; /** * A {@link ReconnectableConnectionProviderWrapper} which makes sure that by the time the * {@link ConnectionProvider#connect()} method is invoked on the delegate, the authorization dance has * been completed and the {@link AuthorizationCodeState} and {@link OAuthCallbackValue} fields have * been properly injected * * @since 4.0 */ public class OAuthConnectionProviderWrapper<C> extends ReconnectableConnectionProviderWrapper<C> implements NoConnectivityTest { private final OAuthConfig oauthConfig; private final Map<Field, String> callbackValues; private final ExtensionsOAuthManager oauthManager; private final FieldSetter<ConnectionProvider<C>, AuthorizationCodeState> authCodeStateSetter; private final Once dance; public OAuthConnectionProviderWrapper(ConnectionProvider<C> delegate, OAuthConfig oauthConfig, Map<Field, String> callbackValues, ExtensionsOAuthManager oauthManager, boolean disableValidation, RetryPolicyTemplate retryPolicyTemplate) { super(delegate, disableValidation, retryPolicyTemplate); this.oauthConfig = oauthConfig; this.oauthManager = oauthManager; authCodeStateSetter = getAuthCodeStateSetter(delegate); this.callbackValues = unmodifiableMap(callbackValues); dance = Once.of(this::updateAuthState); } @Override public C connect() throws ConnectionException { dance.runOnce(); return super.connect(); } public void updateAuthState() { ResourceOwnerOAuthContext context = getContext(); final ConnectionProvider<C> delegate = getDelegate(); authCodeStateSetter.set(delegate, toAuthorizationCodeState(oauthConfig.getAuthCodeConfig(), context)); Map<String, Object> responseParameters = context.getTokenResponseParameters(); callbackValues.keySet().forEach(field -> { String key = field.getName(); if (responseParameters.containsKey(key)) { new FieldSetter<>(field).set(delegate, responseParameters.get(key)); } }); } public String getResourceOwnerId() { return getContext().getResourceOwnerId(); } private FieldSetter<ConnectionProvider<C>, AuthorizationCodeState> getAuthCodeStateSetter(ConnectionProvider<C> delegate) { List<Field> stateFields = getFields(delegate.getClass()).stream() .filter(f -> f.getType().equals(AuthorizationCodeState.class)) .collect(toList()); if (stateFields.size() != 1) { throw new IllegalConnectionProviderModelDefinitionException( format("Connection Provider of class '%s' uses OAuth2 authorization code grant type and thus should contain " + "one (and only one) field of type %s. %d were found", delegate.getClass().getName(), AuthorizationCodeState.class.getName(), stateFields.size())); } return new FieldSetter<>(stateFields.get(0)); } private ResourceOwnerOAuthContext getContext() { return oauthManager.getOAuthContext(oauthConfig) .orElseThrow(() -> new IllegalArgumentException("OAuth authorization dance not yet performed for resourceOwnerId " + oauthConfig.getAuthCodeConfig().getResourceOwnerId())); } @Override public void start() throws MuleException { oauthManager.register(oauthConfig); super.start(); } }