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()>
+;
>>