killbill-memoizeit

tenant: first pass at fixing tenant Signed-off-by: Pierre-Alexandre

11/13/2012 5:14:08 PM

Details

diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 7293b45..cd845e7 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -12,8 +12,7 @@ tableFields(prefix) ::= <<
 , <prefix>created_date
 >>
 
-
-tableValues(prefix) ::= <<
+tableValues() ::= <<
   :accountId
 , :invoiceDate
 , :targetDate
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
index 0453f1f..361b6f5 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/DefaultTenantDao.java
@@ -24,13 +24,14 @@ import org.apache.shiro.authc.SimpleAuthenticationInfo;
 import org.apache.shiro.codec.Base64;
 import org.apache.shiro.crypto.RandomNumberGenerator;
 import org.apache.shiro.crypto.SecureRandomNumberGenerator;
-import org.apache.shiro.crypto.hash.Sha256Hash;
+import org.apache.shiro.crypto.hash.SimpleHash;
 import org.apache.shiro.util.ByteSource;
 import org.skife.jdbi.v2.IDBI;
 
 import com.ning.billing.ErrorCode;
 import com.ning.billing.tenant.api.Tenant;
 import com.ning.billing.tenant.api.TenantApiException;
+import com.ning.billing.tenant.security.KillbillCredentialsMatcher;
 import com.ning.billing.util.callcontext.InternalCallContext;
 import com.ning.billing.util.callcontext.InternalTenantContext;
 import com.ning.billing.util.entity.dao.EntityDaoBase;
@@ -73,15 +74,17 @@ public class DefaultTenantDao extends EntityDaoBase<TenantModelDao, Tenant, Tena
     public void create(final TenantModelDao entity, final InternalCallContext context) throws TenantApiException {
         // Create the salt and password
         final ByteSource salt = rng.nextBytes();
-        // Hash the plain-text password with the random salt and multiple
-        // iterations and then Base64-encode the value (requires less space than Hex):
-        // TODO switch to bcrypt
-        final String hashedPasswordBase64 = new Sha256Hash(entity.getApiSecret(), salt, 1024).toBase64();
+        // Hash the plain-text password with the random salt and multiple iterations and then Base64-encode the value (requires less space than Hex)
+        final String hashedPasswordBase64 = new SimpleHash(KillbillCredentialsMatcher.HASH_ALGORITHM_NAME,
+                                                           entity.getApiSecret(), salt, KillbillCredentialsMatcher.HASH_ITERATIONS).toBase64();
 
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                entitySqlDaoWrapperFactory.become(TenantSqlDao.class).create(entity, hashedPasswordBase64, salt.toBase64(), context);
+                final TenantModelDao tenantModelDaoWithSecret = new TenantModelDao(entity.getId(), context.getCreatedDate(), context.getUpdatedDate(),
+                                                                                   entity.getExternalKey(), entity.getApiKey(),
+                                                                                   hashedPasswordBase64, salt.toBase64());
+                entitySqlDaoWrapperFactory.become(TenantSqlDao.class).create(tenantModelDaoWithSecret, context);
                 return null;
             }
         });
diff --git a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java
index e632f47..6f7b6a2 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/dao/TenantSqlDao.java
@@ -17,15 +17,9 @@
 package com.ning.billing.tenant.dao;
 
 import org.skife.jdbi.v2.sqlobject.Bind;
-import org.skife.jdbi.v2.sqlobject.BindBean;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
-import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 import com.ning.billing.tenant.api.Tenant;
-import com.ning.billing.util.audit.ChangeType;
-import com.ning.billing.util.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalTenantContextBinder;
-import com.ning.billing.util.entity.dao.Audited;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
 
@@ -35,13 +29,6 @@ public interface TenantSqlDao extends EntitySqlDao<TenantModelDao, Tenant> {
     @SqlQuery
     public TenantModelDao getByApiKey(@Bind("apiKey") final String apiKey);
 
-    @SqlUpdate
-    @Audited(ChangeType.INSERT)
-    public void create(@BindBean final TenantModelDao tenant,
-                       @Bind("apiSecret") final String apiSecret,
-                       @Bind("apiSalt") final String apiSalt,
-                       @InternalTenantContextBinder final InternalCallContext context);
-
     @SqlQuery
     public TenantModelDao getSecrets(@Bind("id") final String id);
 }
diff --git a/tenant/src/main/java/com/ning/billing/tenant/security/KillbillCredentialsMatcher.java b/tenant/src/main/java/com/ning/billing/tenant/security/KillbillCredentialsMatcher.java
index eb91eec..9c5dbbb 100644
--- a/tenant/src/main/java/com/ning/billing/tenant/security/KillbillCredentialsMatcher.java
+++ b/tenant/src/main/java/com/ning/billing/tenant/security/KillbillCredentialsMatcher.java
@@ -18,19 +18,22 @@ package com.ning.billing.tenant.security;
 
 import org.apache.shiro.authc.credential.CredentialsMatcher;
 import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
-import org.apache.shiro.crypto.hash.Sha256Hash;
+import org.apache.shiro.crypto.hash.Sha512Hash;
 
 public class KillbillCredentialsMatcher {
 
-    private KillbillCredentialsMatcher() {
-    }
+    // See http://www.stormpath.com/blog/strong-password-hashing-apache-shiro and https://issues.apache.org/jira/browse/SHIRO-290
+    public static final String HASH_ALGORITHM_NAME = Sha512Hash.ALGORITHM_NAME;
+    public static final int HASH_ITERATIONS = 500000;
+
+    private KillbillCredentialsMatcher() {}
 
     public static CredentialsMatcher getCredentialsMatcher() {
         // This needs to be in sync with DefaultTenantDao
-        final HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME);
+        final HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(HASH_ALGORITHM_NAME);
         // base64 encoding, not hex
         credentialsMatcher.setStoredCredentialsHexEncoded(false);
-        credentialsMatcher.setHashIterations(1024);
+        credentialsMatcher.setHashIterations(HASH_ITERATIONS);
 
         return credentialsMatcher;
     }
diff --git a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantSqlDao.sql.stg b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantSqlDao.sql.stg
index 8716c41..56c8986 100644
--- a/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantSqlDao.sql.stg
+++ b/tenant/src/main/resources/com/ning/billing/tenant/dao/TenantSqlDao.sql.stg
@@ -2,7 +2,7 @@ group TenantDaoSql: EntitySqlDao;
 
 tableName() ::= "tenants"
 
-/** Don't add api_secret and api_salt in these fields, we shouldn't need to retrieve them */
+/* Don't add api_secret and api_salt in these fields, we shouldn't need to retrieve them */
 tableFields(prefix) ::= <<
   <prefix>external_key
 , <prefix>api_key
@@ -12,44 +12,55 @@ tableFields(prefix) ::= <<
 , <prefix>updated_by
 >>
 
-/** No account_record_id field */
-accountRecordIdField(prefix) ::= ""
+tableValues() ::= <<
+  :externalKey
+, :apiKey
+, :createdDate
+, :createdBy
+, :updatedDate
+, :updatedBy
+>>
+
+/* No account_record_id field */
+accountRecordIdFieldWithComma(prefix) ::= ""
+accountRecordIdValueWithComma(prefix) ::= ""
 
-/** No tenant_record_id field */
-tenantRecordIdField(prefix) ::= ""
+/* No tenant_record_id field */
+tenantRecordIdFieldWithComma(prefix) ::= ""
+tenantRecordIdValueWithComma(prefix) ::= ""
+CHECK_TENANT(prefix) ::= "1 = 1"
 
+/* Override default create call to include secrets */
 create() ::= <<
-    INSERT INTO tenants (
-        id
-      , external_key
-      , api_key
-      , api_secret
-      , api_salt
-      , created_date
-      , created_by
-      , updated_date
-      , updated_by
-    ) VALUES (
-        :id
-      , :externalKey
-      , :apiKey
-      , :apiSecret
-      , :apiSalt
-      , :createdDate
-      , :userName
-      , :updatedDate
-      , :userName
-    );
+insert into <tableName()> (
+  <idField()>
+, <tableFields()>
+, api_secret
+, api_salt
+)
+values (
+  <idValue()>
+, <tableValues()>
+, :apiSecret
+, :apiSalt
+)
+;
 >>
 
 getByApiKey() ::= <<
-    SELECT <tenantFields()>
-    FROM tenants
-    WHERE api_key = :apiKey;
+select
+  <allTableFields("t.")>
+from <tableName()> t
+where api_key = :apiKey
+;
 >>
 
 getSecrets() ::= <<
-    SELECT api_key, api_secret, api_salt
-    FROM tenants
-    WHERE id = :id;
+select
+  <allTableFields("t.")>
+, t.api_secret
+, t.api_salt
+from <tableName()> t
+where <idField("t.")> = <idValue()>
+;
 >>