killbill-uncached
Changes
.idea/dictionaries/pierre.xml 7(+6 -1)
Details
.idea/dictionaries/pierre.xml 7(+6 -1)
diff --git a/.idea/dictionaries/pierre.xml b/.idea/dictionaries/pierre.xml
index 118b145..3581090 100644
--- a/.idea/dictionaries/pierre.xml
+++ b/.idea/dictionaries/pierre.xml
@@ -1,3 +1,8 @@
<component name="ProjectDictionaryState">
- <dictionary name="pierre" />
+ <dictionary name="pierre">
+ <words>
+ <w>jdbc</w>
+ <w>killbill</w>
+ </words>
+ </dictionary>
</component>
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index 0f7717f..96d6191 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -64,7 +64,7 @@ public class DefaultAccountUserApi implements AccountUserApi {
}
final AccountModelDao account = new AccountModelDao(data);
- accountDao.create(account, internalCallContextFactory.createInternalCallContext(account.getId(), context));
+ accountDao.create(account, internalCallContextFactory.createInternalCallContext(context));
return new DefaultAccount(account);
}
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/extbus/BeatrixListener.java b/beatrix/src/main/java/com/ning/billing/beatrix/extbus/BeatrixListener.java
index d0dc22e..c7fbe39 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/extbus/BeatrixListener.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/extbus/BeatrixListener.java
@@ -37,6 +37,7 @@ import com.ning.billing.util.callcontext.CallOrigin;
import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.events.AccountChangeInternalEvent;
import com.ning.billing.util.events.AccountCreationInternalEvent;
import com.ning.billing.util.events.BusInternalEvent;
@@ -70,17 +71,19 @@ public class
private final PersistentBus externalBus;
private final InternalCallContextFactory internalCallContextFactory;
private final AccountInternalApi accountApi;
-
+ private final NonEntityDao nonEntityDao;
protected final ObjectMapper objectMapper;
@Inject
public BeatrixListener(@Named(BeatrixModule.EXTERNAL_BUS) final PersistentBus externalBus,
final InternalCallContextFactory internalCallContextFactory,
- final AccountInternalApi accountApi) {
+ final AccountInternalApi accountApi,
+ final NonEntityDao nonEntityDao) {
this.externalBus = externalBus;
this.internalCallContextFactory = internalCallContextFactory;
this.accountApi = accountApi;
+ this.nonEntityDao = nonEntityDao;
this.objectMapper = new ObjectMapper();
objectMapper.registerModule(new JodaModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
@@ -224,7 +227,7 @@ public class
default:
}
final UUID accountId = getAccountIdFromRecordId(event.getBusEventType(), objectId, context.getAccountRecordId(), context);
- final UUID tenantId = context.toTenantContext().getTenantId();
+ final UUID tenantId = nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT);
return eventBusType != null ?
new DefaultBusExternalEvent(objectId, objectType, eventBusType, accountId, tenantId, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()) :
diff --git a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
index 1e5c462..6e425a2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/InvoiceDispatcher.java
@@ -36,6 +36,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ning.billing.ErrorCode;
+import com.ning.billing.ObjectType;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.bus.api.PersistentBus;
@@ -64,6 +65,9 @@ import com.ning.billing.invoice.model.FixedPriceInvoiceItem;
import com.ning.billing.invoice.model.RecurringInvoiceItem;
import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.clock.Clock;
+import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.events.BusInternalEvent;
import com.ning.billing.util.events.EffectiveSubscriptionInternalEvent;
import com.ning.billing.util.events.InvoiceAdjustmentInternalEvent;
@@ -90,6 +94,7 @@ public class InvoiceDispatcher {
private final AccountInternalApi accountApi;
private final SubscriptionInternalApi subscriptionApi;
private final InvoiceDao invoiceDao;
+ private final NonEntityDao nonEntityDao;
private final InvoiceNotifier invoiceNotifier;
private final GlobalLocker locker;
private final PersistentBus eventBus;
@@ -100,6 +105,7 @@ public class InvoiceDispatcher {
final BillingInternalApi billingApi,
final SubscriptionInternalApi SubscriptionApi,
final InvoiceDao invoiceDao,
+ final NonEntityDao nonEntityDao,
final InvoiceNotifier invoiceNotifier,
final GlobalLocker locker,
final PersistentBus eventBus,
@@ -109,6 +115,7 @@ public class InvoiceDispatcher {
this.subscriptionApi = SubscriptionApi;
this.accountApi = accountApi;
this.invoiceDao = invoiceDao;
+ this.nonEntityDao = nonEntityDao;
this.invoiceNotifier = invoiceNotifier;
this.locker = locker;
this.eventBus = eventBus;
@@ -255,7 +262,7 @@ public class InvoiceDispatcher {
if (account.isNotifiedForInvoices() && invoice != null && !dryRun) {
// Need to re-hydrate the invoice object to get the invoice number (record id)
// API_FIX InvoiceNotifier public API?
- invoiceNotifier.notify(account, new DefaultInvoice(invoiceDao.getById(invoice.getId(), context)), context.toTenantContext());
+ invoiceNotifier.notify(account, new DefaultInvoice(invoiceDao.getById(invoice.getId(), context)), buildTenantContext(context));
}
return invoice;
@@ -265,6 +272,9 @@ public class InvoiceDispatcher {
}
}
+ private TenantContext buildTenantContext(final InternalTenantContext context) {
+ return context.toTenantContext(nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT));
+ }
@VisibleForTesting
Map<UUID, DateTime> createNextFutureNotificationDate(final List<InvoiceItemModelDao> invoiceItems, final DateAndTimeZoneContext dateAndTimeZoneContext) {
diff --git a/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java b/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
index 33b5b93..93a00f4 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/InvoiceTestSuiteWithEmbeddedDB.java
@@ -43,6 +43,7 @@ import com.ning.billing.util.api.TagUserApi;
import com.ning.billing.util.cache.CacheControllerDispatcher;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.clock.Clock;
+import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.svcapi.account.AccountInternalApi;
import com.ning.billing.util.svcapi.subscription.SubscriptionInternalApi;
import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
@@ -84,6 +85,8 @@ public abstract class InvoiceTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
@Inject
protected InvoiceDao invoiceDao;
@Inject
+ protected NonEntityDao nonEntityDao;
+ @Inject
protected TagUserApi tagUserApi;
@Inject
protected GlobalLocker locker;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
index 467cf0a..90fbb11 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceDispatcher.java
@@ -89,7 +89,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
- invoiceNotifier, locker, busService.getBus(),
+ nonEntityDao, invoiceNotifier, locker, busService.getBus(),
clock);
Invoice invoice = dispatcher.processAccount(accountId, target, true, internalCallContext);
@@ -143,7 +143,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
Mockito.when(billingApi.getBillingEventsForAccountAndUpdateAccountBCD(Mockito.<UUID>any(), Mockito.<InternalCallContext>any())).thenReturn(events);
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
- invoiceNotifier, locker, busService.getBus(),
+ nonEntityDao, invoiceNotifier, locker, busService.getBus(),
clock);
final Invoice invoice = dispatcher.processAccount(account.getId(), new DateTime("2012-07-30T00:00:00.000Z"), false, internalCallContext);
@@ -203,7 +203,7 @@ public class TestInvoiceDispatcher extends InvoiceTestSuiteWithEmbeddedDB {
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi, invoiceDao,
- invoiceNotifier, locker, busService.getBus(),
+ nonEntityDao, invoiceNotifier, locker, busService.getBus(),
clock);
final DateTime expectedBefore = clock.getUTCNow();
diff --git a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceHelper.java b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceHelper.java
index e2d5357..d078351 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceHelper.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/TestInvoiceHelper.java
@@ -60,6 +60,7 @@ import com.ning.billing.subscription.api.user.SubscriptionUserApiException;
import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalTenantContext;
import com.ning.billing.clock.Clock;
+import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.entity.EntityPersistenceException;
import com.ning.billing.util.svcapi.account.AccountInternalApi;
import com.ning.billing.util.svcapi.subscription.SubscriptionInternalApi;
@@ -135,6 +136,7 @@ public class TestInvoiceHelper {
private final GlobalLocker locker;
private final Clock clock;
private final InternalCallContext internalCallContext;
+ private final NonEntityDao nonEntityDao;
// Low level SqlDao used by the tests to directly insert rows
private final InvoicePaymentSqlDao invoicePaymentSqlDao;
@@ -145,7 +147,7 @@ public class TestInvoiceHelper {
@Inject
public TestInvoiceHelper(final InvoiceGenerator generator, final IDBI dbi,
final BillingInternalApi billingApi, final AccountInternalApi accountApi, final SubscriptionInternalApi subscriptionApi, final BusService busService,
- final InvoiceDao invoiceDao, final GlobalLocker locker, final Clock clock, final InternalCallContext internalCallContext) {
+ final InvoiceDao invoiceDao, final GlobalLocker locker, final Clock clock, final InternalCallContext internalCallContext, final NonEntityDao nonEntityDao) {
this.generator = generator;
this.billingApi = billingApi;
this.accountApi = accountApi;
@@ -155,6 +157,7 @@ public class TestInvoiceHelper {
this.locker = locker;
this.clock = clock;
this.internalCallContext = internalCallContext;
+ this.nonEntityDao = nonEntityDao;
this.invoiceItemSqlDao = dbi.onDemand(InvoiceItemSqlDao.class);
this.invoicePaymentSqlDao = dbi.onDemand(InvoicePaymentSqlDao.class);
}
@@ -177,7 +180,7 @@ public class TestInvoiceHelper {
final InvoiceNotifier invoiceNotifier = new NullInvoiceNotifier();
final InvoiceDispatcher dispatcher = new InvoiceDispatcher(generator, accountApi, billingApi, subscriptionApi,
- invoiceDao, invoiceNotifier, locker, busService.getBus(),
+ invoiceDao, nonEntityDao, invoiceNotifier, locker, busService.getBus(),
clock);
Invoice invoice = dispatcher.processAccount(account.getId(), targetDate, true, internalCallContext);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index fc53f23..c1c2817 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -29,6 +29,12 @@ public interface JaxrsResource {
public static final String SEARCH = "search";
/*
+ * Multi-Tenancy headers
+ */
+ public static String HDR_API_KEY = "X-Killbill-ApiKey";
+ public static String HDR_API_SECRET = "X-Killbill-ApiSecret";
+
+ /*
* Metadata Additional headers
*/
public static String HDR_CREATED_BY = "X-Killbill-CreatedBy";
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
index 18b95b7..6776382 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/util/Context.java
@@ -67,6 +67,7 @@ public class Context {
}
private Tenant getTenantFromRequest(final ServletRequest request) {
+ // See com.ning.billing.server.security.TenantFilter
final Object tenantObject = request.getAttribute("killbill_tenant");
if (tenantObject == null) {
return null;
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
index c2b3e15..c72e410 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
@@ -50,6 +50,7 @@ import com.ning.billing.payment.provider.DefaultPaymentMethodInfoPlugin;
import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.svcapi.account.AccountInternalApi;
import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
import com.ning.billing.util.svcapi.tag.TagInternalApi;
@@ -72,10 +73,11 @@ public class PaymentMethodProcessor extends ProcessorBase {
final InvoiceInternalApi invoiceApi,
final PersistentBus eventBus,
final PaymentDao paymentDao,
+ final NonEntityDao nonEntityDao,
final TagInternalApi tagUserApi,
final GlobalLocker locker,
@Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor) {
- super(pluginRegistry, accountInternalApi, eventBus, paymentDao, tagUserApi, locker, executor, invoiceApi);
+ super(pluginRegistry, accountInternalApi, eventBus, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi);
}
public Set<String> getAvailablePlugins() {
@@ -139,7 +141,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
if (withPluginInfo) {
try {
final PaymentPluginApi pluginApi = getPaymentPluginApi(paymentMethodModelDao.getPluginName());
- paymentMethodPlugin = pluginApi.getPaymentMethodDetail(paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId(), context.toTenantContext());
+ paymentMethodPlugin = pluginApi.getPaymentMethodDetail(paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId(), buildTenantContext(context));
} catch (PaymentPluginApiException e) {
log.warn("Error retrieving payment method " + paymentMethodModelDao.getId() + " from plugin " + paymentMethodModelDao.getPluginName(), e);
throw new PaymentApiException(ErrorCode.PAYMENT_GET_PAYMENT_METHODS, paymentMethodModelDao.getAccountId(), paymentMethodModelDao.getId());
@@ -171,7 +173,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
final List<PaymentMethodPlugin> paymentMethods;
try {
- paymentMethods = pluginApi.searchPaymentMethods(searchKey, internalTenantContext.toTenantContext());
+ paymentMethods = pluginApi.searchPaymentMethods(searchKey, buildTenantContext(internalTenantContext));
} catch (PaymentPluginApiException e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENT_METHODS, pluginName, searchKey);
}
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
index 13c1b27..81ea1b5 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentProcessor.java
@@ -20,6 +20,7 @@ import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.inject.name.Named;
import com.ning.billing.ErrorCode;
+import com.ning.billing.ObjectType;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.bus.api.PersistentBus;
@@ -48,7 +49,9 @@ import com.ning.billing.payment.retry.FailedPaymentRetryService.FailedPaymentRet
import com.ning.billing.payment.retry.PluginFailureRetryService.PluginFailureRetryServiceScheduler;
import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.callcontext.TenantContext;
import com.ning.billing.util.config.PaymentConfig;
+import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.events.BusInternalEvent;
import com.ning.billing.util.events.PaymentErrorInternalEvent;
import com.ning.billing.util.svcapi.account.AccountInternalApi;
@@ -99,12 +102,13 @@ public class PaymentProcessor extends ProcessorBase {
final PluginFailureRetryServiceScheduler pluginFailureRetryService,
final AutoPayRetryServiceScheduler autoPayoffRetryService,
final PaymentDao paymentDao,
+ final NonEntityDao nonEntityDao,
final PersistentBus eventBus,
final Clock clock,
final GlobalLocker locker,
final PaymentConfig paymentConfig,
@Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor) {
- super(pluginRegistry, accountUserApi, eventBus, paymentDao, tagUserApi, locker, executor, invoiceApi);
+ super(pluginRegistry, accountUserApi, eventBus, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi);
this.paymentMethodProcessor = paymentMethodProcessor;
this.failedPaymentRetryService = failedPaymentRetryService;
this.pluginFailureRetryService = pluginFailureRetryService;
@@ -125,7 +129,7 @@ public class PaymentProcessor extends ProcessorBase {
PaymentInfoPlugin pluginInfo = null;
if (plugin != null) {
try {
- pluginInfo = plugin.getPaymentInfo(model.getAccountId(), paymentId, context.toTenantContext());
+ pluginInfo = plugin.getPaymentInfo(model.getAccountId(), paymentId, buildTenantContext(context));
} catch (PaymentPluginApiException e) {
throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_GET_PAYMENT_INFO, paymentId, e.toString());
}
@@ -133,7 +137,6 @@ public class PaymentProcessor extends ProcessorBase {
return fromPaymentModelDao(model, pluginInfo, context);
}
-
public List<Payment> getInvoicePayments(final UUID invoiceId, final InternalTenantContext context) {
return getPayments(paymentDao.getPaymentsForInvoice(invoiceId, context), context);
}
diff --git a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
index 7d0801c..2b67354 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/ProcessorBase.java
@@ -44,6 +44,8 @@ import com.ning.billing.payment.plugin.api.PaymentPluginApi;
import com.ning.billing.util.api.TagApiException;
import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalTenantContext;
+import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.events.BusInternalEvent;
import com.ning.billing.util.globallocker.LockerType;
import com.ning.billing.util.svcapi.account.AccountInternalApi;
@@ -65,6 +67,7 @@ public abstract class ProcessorBase {
protected final GlobalLocker locker;
protected final ExecutorService executor;
protected final PaymentDao paymentDao;
+ protected final NonEntityDao nonEntityDao;
protected final TagInternalApi tagInternalApi;
private static final Logger log = LoggerFactory.getLogger(ProcessorBase.class);
@@ -74,6 +77,7 @@ public abstract class ProcessorBase {
final AccountInternalApi accountInternalApi,
final PersistentBus eventBus,
final PaymentDao paymentDao,
+ final NonEntityDao nonEntityDao,
final TagInternalApi tagInternalApi,
final GlobalLocker locker,
final ExecutorService executor, final InvoiceInternalApi invoiceApi) {
@@ -81,6 +85,7 @@ public abstract class ProcessorBase {
this.accountInternalApi = accountInternalApi;
this.eventBus = eventBus;
this.paymentDao = paymentDao;
+ this.nonEntityDao = nonEntityDao;
this.locker = locker;
this.executor = executor;
this.tagInternalApi = tagInternalApi;
@@ -150,6 +155,10 @@ public abstract class ProcessorBase {
return invoice;
}
+ protected TenantContext buildTenantContext(final InternalTenantContext context) {
+ return context.toTenantContext(nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT));
+ }
+
public interface WithAccountLockCallback<T> {
public T doOperation() throws PaymentApiException;
diff --git a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
index 3e529b5..413a9c5 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/RefundProcessor.java
@@ -56,6 +56,7 @@ import com.ning.billing.util.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.callcontext.InternalTenantContext;
import com.ning.billing.util.callcontext.UserType;
+import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.svcapi.account.AccountInternalApi;
import com.ning.billing.util.svcapi.invoice.InvoiceInternalApi;
import com.ning.billing.util.svcapi.tag.TagInternalApi;
@@ -83,9 +84,10 @@ public class RefundProcessor extends ProcessorBase {
final InternalCallContextFactory internalCallContextFactory,
final TagInternalApi tagUserApi,
final PaymentDao paymentDao,
+ final NonEntityDao nonEntityDao,
final GlobalLocker locker,
@Named(PLUGIN_EXECUTOR_NAMED) final ExecutorService executor) {
- super(pluginRegistry, accountApi, eventBus, paymentDao, tagUserApi, locker, executor, invoiceApi);
+ super(pluginRegistry, accountApi, eventBus, paymentDao, nonEntityDao, tagUserApi, locker, executor, invoiceApi);
this.internalCallContextFactory = internalCallContextFactory;
}
diff --git a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
index 6337238..ba05640 100644
--- a/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
+++ b/server/src/main/java/com/ning/billing/server/listeners/KillbillGuiceListener.java
@@ -66,7 +66,7 @@ public class KillbillGuiceListener extends SetupServer {
@Override
public void contextInitialized(final ServletContextEvent event) {
- final boolean multitenant = Boolean.parseBoolean(System.getProperty(KILLBILL_MULTITENANT_PROPERTY, "false"));
+ final boolean multitenant = Boolean.parseBoolean(System.getProperty(KILLBILL_MULTITENANT_PROPERTY, "true"));
final ServerModuleBuilder builder = new ServerModuleBuilder()
.addConfig(KillbillServerConfig.class)
diff --git a/server/src/main/java/com/ning/billing/server/security/TenantFilter.java b/server/src/main/java/com/ning/billing/server/security/TenantFilter.java
index 98a77aa..2eaa1a4 100644
--- a/server/src/main/java/com/ning/billing/server/security/TenantFilter.java
+++ b/server/src/main/java/com/ning/billing/server/security/TenantFilter.java
@@ -26,20 +26,28 @@ import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
-import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.subject.Subject;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
+import org.apache.shiro.realm.Realm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.ning.billing.jaxrs.resources.JaxrsResource;
import com.ning.billing.tenant.api.Tenant;
import com.ning.billing.tenant.api.TenantApiException;
import com.ning.billing.tenant.api.TenantUserApi;
+import com.google.common.collect.ImmutableList;
+
@Singleton
public class TenantFilter implements Filter {
- public static final String SUBJECT = "killbill_subject";
+ // See com.ning.billing.jaxrs.util.Context
public static final String TENANT = "killbill_tenant";
private static final Logger log = LoggerFactory.getLogger(TenantFilter.class);
@@ -47,23 +55,55 @@ public class TenantFilter implements Filter {
@Inject
private TenantUserApi tenantUserApi;
+ private final ModularRealmAuthenticator modularRealmAuthenticator;
+
+ public TenantFilter() {
+ final Realm killbillJdbcRealm = new KillbillJdbcRealm();
+
+ // We use Shiro to verify the api credentials - but the Shiro Subject is only used for RBAC
+ modularRealmAuthenticator = new ModularRealmAuthenticator();
+ modularRealmAuthenticator.setRealms(ImmutableList.<Realm>of(killbillJdbcRealm));
+ }
+
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
- final Subject subject = SecurityUtils.getSubject();
- request.setAttribute(SUBJECT, subject);
-
- final String apiKey = (String) subject.getPrincipal();
- if (apiKey == null) {
- // Resource not protected by Shiro?
+ if (shouldSkipFilter(request)) {
chain.doFilter(request, response);
return;
}
+ // Lookup tenant information in the headers
+ String apiKey = null;
+ String apiSecret = null;
+ if (request instanceof HttpServletRequest) {
+ final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ apiKey = httpServletRequest.getHeader(JaxrsResource.HDR_API_KEY);
+ apiSecret = httpServletRequest.getHeader(JaxrsResource.HDR_API_SECRET);
+ }
+
+ // Multi-tenancy is enabled if this filter is installed, we can't continue without credentials
+ if (apiKey == null || apiSecret == null) {
+ final String errorMessage = String.format("Make sure to set the %s and %s headers", JaxrsResource.HDR_API_KEY, JaxrsResource.HDR_API_SECRET);
+ sendAuthError(response, errorMessage);
+ return;
+ }
+
+ // Verify the apiKey/apiSecret combo
+ final AuthenticationToken token = new UsernamePasswordToken(apiKey, apiSecret);
try {
+ modularRealmAuthenticator.authenticate(token);
+ } catch (AuthenticationException e) {
+ final String errorMessage = e.getLocalizedMessage();
+ sendAuthError(response, errorMessage);
+ return;
+ }
+
+ try {
+ // Load the tenant in the request object (apiKey is unique across tenants)
final Tenant tenant = tenantUserApi.getTenantByApiKey(apiKey);
request.setAttribute(TENANT, tenant);
@@ -77,4 +117,26 @@ public class TenantFilter implements Filter {
@Override
public void destroy() {
}
+
+ private boolean shouldSkipFilter(final ServletRequest request) {
+ boolean shouldSkip = false;
+
+ // Chicken - egg problem
+ if (request instanceof HttpServletRequest) {
+ final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ final String path = httpServletRequest.getRequestURI();
+ if ("/1.0/kb/tenants".equals(path) && "POST".equals(httpServletRequest.getMethod())) {
+ shouldSkip = true;
+ }
+ }
+
+ return shouldSkip;
+ }
+
+ private void sendAuthError(final ServletResponse response, final String errorMessage) throws IOException {
+ if (response instanceof HttpServletResponse) {
+ final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+ httpServletResponse.sendError(401, errorMessage);
+ }
+ }
}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
index 80d7944..4534858 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/KillbillClient.java
@@ -78,6 +78,8 @@ import com.google.common.collect.ImmutableMap;
import static com.ning.billing.jaxrs.resources.JaxrsResource.ACCOUNTS;
import static com.ning.billing.jaxrs.resources.JaxrsResource.BUNDLES;
+import static com.ning.billing.jaxrs.resources.JaxrsResource.HDR_API_KEY;
+import static com.ning.billing.jaxrs.resources.JaxrsResource.HDR_API_SECRET;
import static com.ning.billing.jaxrs.resources.JaxrsResource.QUERY_DELETE_DEFAULT_PM_WITH_AUTO_PAY_OFF;
import static com.ning.billing.jaxrs.resources.JaxrsResource.SUBSCRIPTIONS;
import static org.testng.Assert.assertEquals;
@@ -87,7 +89,7 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
protected static final String PLUGIN_NAME = "noop";
- protected static final int DEFAULT_HTTP_TIMEOUT_SEC = 5;
+ protected static final int DEFAULT_HTTP_TIMEOUT_SEC = 20;
protected static final Map<String, String> DEFAULT_EMPTY_QUERY = new HashMap<String, String>();
@@ -102,6 +104,12 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
protected AsyncHttpClient httpClient;
protected ObjectMapper mapper;
+ // Multi-Tenancy information, if enabled
+ protected String DEFAULT_API_KEY = UUID.randomUUID().toString();
+ protected String DEFAULT_API_SECRET = UUID.randomUUID().toString();
+ protected String apiKey = DEFAULT_API_KEY;
+ protected String apiSecret = DEFAULT_API_SECRET;
+
// Context information to be passed around
protected static final String createdBy = "Toto";
protected static final String reason = "i am god";
@@ -1031,6 +1039,8 @@ public abstract class KillbillClient extends GuicyKillbillTestSuiteWithEmbeddedD
}
builder.addHeader(HEADER_CONTENT_TYPE, CONTENT_TYPE);
+ builder.addHeader(HDR_API_KEY, apiKey);
+ builder.addHeader(HDR_API_SECRET, apiSecret);
for (final Entry<String, String> q : queryParams.entrySet()) {
builder.addQueryParameter(q.getKey(), q.getValue());
}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
index 73b0a44..239c4d3 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestAccount.java
@@ -257,7 +257,8 @@ public class TestAccount extends TestJaxrsBase {
@Test(groups = "slow")
public void testTags() throws Exception {
- final String accountId = UUID.randomUUID().toString();
+ final AccountJson input = createAccount();
+ final String accountId = input.getAccountId();
final String uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountId + "/" + JaxrsResource.TAGS;
final String accountTagsUrl = getUrlFromUri(uri);
// Use tag definition for AUTO_PAY_OFF
@@ -287,7 +288,6 @@ public class TestAccount extends TestJaxrsBase {
@Test(groups = "slow")
public void testCustomFields() throws Exception {
-
final AccountJson accountJson = createAccount("yoyoq", "gfgrqe", "yoyoq@yahoo.com");
assertNotNull(accountJson);
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index e1dff41..34ea64e 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -234,6 +234,9 @@ public class TestJaxrsBase extends KillbillClient {
busHandler.reset();
clock.resetDeltaFromReality();
clock.setDay(new LocalDate(2012, 8, 25));
+
+ // Recreate the tenant (tables have been cleaned-up)
+ createTenant(DEFAULT_API_KEY, DEFAULT_API_SECRET);
}
@AfterMethod(groups = "slow")
@@ -246,16 +249,14 @@ public class TestJaxrsBase extends KillbillClient {
public void beforeClass() throws Exception {
loadConfig();
-
listener.getInstantiatedInjector().injectMembers(this);
httpClient = new AsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(DEFAULT_HTTP_TIMEOUT_SEC * 1000).build());
+
mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
- //mapper.setPropertyNamingStrategy(new PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy());
-
busHandler = new TestApiListener(null, dbi);
}
@@ -277,13 +278,10 @@ public class TestJaxrsBase extends KillbillClient {
loadConfig();
listener = new TestKillbillGuiceListener(helper);
- server = new HttpServer();
+ server = new HttpServer();
server.configure(config, getListeners(), getFilters());
-
server.start();
-
- listener.getInstantiatedInjector().injectMembers(this);
}
protected Iterable<EventListener> getListeners() {
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java b/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java
index 83ab8cf..68d5a6d 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestPushNotification.java
@@ -68,7 +68,7 @@ public class TestPushNotification extends TestJaxrsBase {
private boolean waitForCallbacksToComplete() throws InterruptedException {
- long remainingMs = 5000;
+ long remainingMs = 20000;
do {
if (callbackCompleted) {
break;
diff --git a/server/src/test/java/com/ning/billing/server/security/TestTenantFilter.java b/server/src/test/java/com/ning/billing/server/security/TestTenantFilter.java
index d4a0dbf..ef60130 100644
--- a/server/src/test/java/com/ning/billing/server/security/TestTenantFilter.java
+++ b/server/src/test/java/com/ning/billing/server/security/TestTenantFilter.java
@@ -16,56 +16,30 @@
package com.ning.billing.server.security;
-import java.util.EventListener;
-import java.util.Iterator;
-import java.util.Map;
-
import javax.ws.rs.core.Response.Status;
-import org.apache.shiro.web.env.EnvironmentLoaderListener;
-import org.apache.shiro.web.servlet.ShiroFilter;
-import org.eclipse.jetty.servlet.FilterHolder;
import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.ning.billing.jaxrs.TestJaxrsBase;
import com.ning.billing.jaxrs.json.AccountJson;
import com.ning.billing.server.listeners.KillbillGuiceListener;
-import com.ning.http.client.AsyncHttpClient;
-import com.ning.http.client.AsyncHttpClientConfig;
-import com.ning.http.client.Realm;
-import com.ning.http.client.Realm.AuthScheme;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-public class TestTenantFilter extends TestJaxrsBase {
-
- @Override
- protected void loadConfig() {
- System.setProperty(KillbillGuiceListener.KILLBILL_MULTITENANT_PROPERTY, "true");
- super.loadConfig();
- }
+// Pure Multi-Tenancy test (no RBAC)
+public class TestTenantFilter extends TestJaxrsBase {
- @Override
- protected Iterable<EventListener> getListeners() {
- return new Iterable<EventListener>() {
- @Override
- public Iterator<EventListener> iterator() {
- return ImmutableList.<EventListener>of(listener, new EnvironmentLoaderListener()).iterator();
- }
- };
+ @AfterMethod(groups = "slow")
+ public void tearDown() throws Exception {
+ // Default credentials
+ loginTenant(DEFAULT_API_KEY, DEFAULT_API_SECRET);
}
- @Override
- protected Map<FilterHolder, String> getFilters() {
- return ImmutableMap.<FilterHolder, String>of(new FilterHolder(ShiroFilter.class), "/*");
- }
-
- // TODO Need to run by itself for now as the server from the test suite doesn't have the Shiro setup
- @Test(groups = "slow", enabled = false)
+ @Test(groups = "slow")
public void testTenantShouldOnlySeeOwnAccount() throws Exception {
// Try to create an account without being logged-in
+ logoutTenant();
Assert.assertEquals(createAccountNoValidation().getStatusCode(), Status.UNAUTHORIZED.getStatusCode());
// Create the tenant
@@ -105,18 +79,12 @@ public class TestTenantFilter extends TestJaxrsBase {
}
private void loginTenant(final String apiKey, final String apiSecret) {
- final AsyncHttpClientConfig.Builder builder = new AsyncHttpClientConfig.Builder();
- final Realm realm = new Realm.RealmBuilder()
- .setPrincipal(apiKey)
- .setPassword(apiSecret)
- .setUsePreemptiveAuth(true)
- .setScheme(AuthScheme.BASIC)
- .build();
- builder.setRealm(realm).setRequestTimeoutInMs(DEFAULT_HTTP_TIMEOUT_SEC * 1000).build();
- httpClient = new AsyncHttpClient(builder.build());
+ this.apiKey = apiKey;
+ this.apiSecret = apiSecret;
}
private void logoutTenant() {
- httpClient = new AsyncHttpClient(new AsyncHttpClientConfig.Builder().setRequestTimeoutInMs(DEFAULT_HTTP_TIMEOUT_SEC * 1000).build());
+ apiKey = "";
+ apiSecret = "";
}
}
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/DefaultTenantContext.java b/util/src/main/java/com/ning/billing/util/callcontext/DefaultTenantContext.java
index 4d072d2..a6dbf20 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/DefaultTenantContext.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/DefaultTenantContext.java
@@ -24,10 +24,6 @@ public class DefaultTenantContext implements TenantContext {
private final UUID tenantId;
- public DefaultTenantContext() {
- this(null);
- }
-
public DefaultTenantContext(@Nullable final UUID tenantId) {
this.tenantId = tenantId;
}
diff --git a/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContext.java b/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContext.java
index 5454264..7dacd5e 100644
--- a/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContext.java
+++ b/util/src/main/java/com/ning/billing/util/callcontext/InternalTenantContext.java
@@ -16,6 +16,8 @@
package com.ning.billing.util.callcontext;
+import java.util.UUID;
+
import javax.annotation.Nullable;
/**
@@ -35,8 +37,8 @@ public class InternalTenantContext {
this(defaultTenantRecordId, null);
}
- public TenantContext toTenantContext() {
- return new DefaultTenantContext();
+ public TenantContext toTenantContext(final UUID tenantId) {
+ return new DefaultTenantContext(tenantId);
}
public Long getAccountRecordId() {
diff --git a/util/src/main/java/com/ning/billing/util/dao/DefaultNonEntityDao.java b/util/src/main/java/com/ning/billing/util/dao/DefaultNonEntityDao.java
index 232983e..5631dbc 100644
--- a/util/src/main/java/com/ning/billing/util/dao/DefaultNonEntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/DefaultNonEntityDao.java
@@ -103,6 +103,11 @@ public class DefaultNonEntityDao implements NonEntityDao {
return nonEntitySqlDao.getHistoryTargetRecordId(recordId, tableName.getTableName());
}
+ @Override
+ public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType) {
+ final TableName tableName = TableName.fromObjectType(objectType);
+ return nonEntitySqlDao.getIdFromObject(recordId, tableName.getTableName());
+ }
private interface OperationRetrieval<T> {
public T doRetrieve(final UUID objectId, final ObjectType objectType);
diff --git a/util/src/main/java/com/ning/billing/util/dao/NonEntityDao.java b/util/src/main/java/com/ning/billing/util/dao/NonEntityDao.java
index 26be4a4..58f7ae3 100644
--- a/util/src/main/java/com/ning/billing/util/dao/NonEntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/NonEntityDao.java
@@ -39,6 +39,8 @@ public interface NonEntityDao {
// This retrieves from the history table the latest record for which targetId matches the one we are passing
public Long retrieveLastHistoryRecordIdFromTransaction(final Long targetRecordId, final TableName tableName, final NonEntitySqlDao transactional);
- //This is the reverse from retrieveLastHistoryRecordIdFromTransaction; this retrieves the record_id of the object matching a given history row
+ // This is the reverse from retrieveLastHistoryRecordIdFromTransaction; this retrieves the record_id of the object matching a given history row
public Long retrieveHistoryTargetRecordId(final Long recordId, final TableName tableName);
+
+ public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType);
}
diff --git a/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java b/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java
index f0a119d..36d7811 100644
--- a/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/dao/NonEntitySqlDao.java
@@ -16,6 +16,8 @@
package com.ning.billing.util.dao;
+import java.util.UUID;
+
import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.customizers.Define;
@@ -30,6 +32,9 @@ public interface NonEntitySqlDao extends Transactional<NonEntitySqlDao>, CloseMe
public Long getRecordIdFromObject(@Bind("id") String id, @Define("tableName") final String tableName);
@SqlQuery
+ public UUID getIdFromObject(@Bind("recordId") Long recordId, @Define("tableName") final String tableName);
+
+ @SqlQuery
public Long getAccountRecordIdFromAccount(@Bind("id") String id);
@SqlQuery
diff --git a/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg
index 3e11edb..1969615 100644
--- a/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/dao/NonEntitySqlDao.sql.stg
@@ -8,6 +8,14 @@ where id = :id
;
>>
+getIdFromObject(tableName) ::= <<
+select
+ id
+from <tableName>
+where record_id = :recordId
+;
+>>
+
getAccountRecordIdFromAccountHistory() ::= <<
select
target_record_id
diff --git a/util/src/test/java/com/ning/billing/dao/MockNonEntityDao.java b/util/src/test/java/com/ning/billing/dao/MockNonEntityDao.java
index 4444bdf..3bb80f9 100644
--- a/util/src/test/java/com/ning/billing/dao/MockNonEntityDao.java
+++ b/util/src/test/java/com/ning/billing/dao/MockNonEntityDao.java
@@ -52,4 +52,9 @@ public class MockNonEntityDao implements NonEntityDao {
public Long retrieveHistoryTargetRecordId(final Long recordId, final TableName tableName) {
return null;
}
+
+ @Override
+ public UUID retrieveIdFromObject(final Long recordId, final ObjectType objectType) {
+ return null;
+ }
}