blob: a54b003b162fc490aa8c5df0d44a44d467a02a22 [file] [log] [blame]
/*
* Copyright 2017 The Mifos Initiative.
*
* Licensed 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 CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.mifos.identity.internal.repository;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.exceptions.InvalidQueryException;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Update;
import com.datastax.driver.core.schemabuilder.Create;
import com.datastax.driver.core.schemabuilder.SchemaBuilder;
import com.datastax.driver.core.schemabuilder.SchemaStatement;
import com.datastax.driver.mapping.Mapper;
import io.mifos.core.cassandra.core.CassandraSessionProvider;
import io.mifos.core.cassandra.core.TenantAwareCassandraMapperProvider;
import io.mifos.core.lang.security.RsaKeyPairFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* @author Myrle Krantz
*/
@Component
public class Signatures {
static final String TABLE_NAME = "isis_signatures";
private static final String INDEX_NAME = "isis_signatures_valid_index";
static final String KEY_TIMESTAMP_COLUMN = "key_timestamp";
static final String VALID_COLUMN = "valid";
static final String PRIVATE_KEY_MOD_COLUMN = "private_key_mod";
static final String PRIVATE_KEY_EXP_COLUMN = "private_key_exp";
static final String PUBLIC_KEY_MOD_COLUMN = "public_key_mod";
static final String PUBLIC_KEY_EXP_COLUMN = "public_key_exp";
private final CassandraSessionProvider cassandraSessionProvider;
private final TenantAwareCassandraMapperProvider tenantAwareCassandraMapperProvider;
@Autowired
public Signatures(
final CassandraSessionProvider cassandraSessionProvider,
final TenantAwareCassandraMapperProvider tenantAwareCassandraMapperProvider) {
this.cassandraSessionProvider = cassandraSessionProvider;
this.tenantAwareCassandraMapperProvider = tenantAwareCassandraMapperProvider;
}
public void buildTable() {
final Create create = SchemaBuilder.createTable(TABLE_NAME)
.ifNotExists()
.addPartitionKey(KEY_TIMESTAMP_COLUMN, DataType.text())
.addColumn(VALID_COLUMN, DataType.cboolean())
.addColumn(PRIVATE_KEY_MOD_COLUMN, DataType.varint())
.addColumn(PRIVATE_KEY_EXP_COLUMN, DataType.varint())
.addColumn(PUBLIC_KEY_MOD_COLUMN, DataType.varint())
.addColumn(PUBLIC_KEY_EXP_COLUMN, DataType.varint());
cassandraSessionProvider.getTenantSession().execute(create);
final SchemaStatement createValidIndex = SchemaBuilder.createIndex(INDEX_NAME)
.ifNotExists()
.onTable(TABLE_NAME)
.andColumn(VALID_COLUMN);
cassandraSessionProvider.getTenantSession().execute(createValidIndex);
}
public SignatureEntity add(final RsaKeyPairFactory.KeyPairHolder keys)
{
//There will only be one entry in this table.
final BoundStatement tenantCreationStatement =
cassandraSessionProvider.getTenantSession().prepare("INSERT INTO " + TABLE_NAME + " ("
+ KEY_TIMESTAMP_COLUMN + ", "
+ VALID_COLUMN + ", "
+ PRIVATE_KEY_MOD_COLUMN + ", "
+ PRIVATE_KEY_EXP_COLUMN + ", "
+ PUBLIC_KEY_MOD_COLUMN + ", "
+ PUBLIC_KEY_EXP_COLUMN + ")"
+ "VALUES (?, ?, ?, ?, ?, ?)").bind();
tenantCreationStatement.setString(KEY_TIMESTAMP_COLUMN, keys.getTimestamp());
tenantCreationStatement.setBool(VALID_COLUMN, true);
tenantCreationStatement.setVarint(PRIVATE_KEY_MOD_COLUMN, keys.getPrivateKeyMod());
tenantCreationStatement.setVarint(PRIVATE_KEY_EXP_COLUMN, keys.getPrivateKeyExp());
tenantCreationStatement.setVarint(PUBLIC_KEY_MOD_COLUMN, keys.getPublicKeyMod());
tenantCreationStatement.setVarint(PUBLIC_KEY_EXP_COLUMN, keys.getPublicKeyExp());
cassandraSessionProvider.getTenantSession().execute(tenantCreationStatement);
final SignatureEntity ret = new SignatureEntity();
ret.setKeyTimestamp(keys.getTimestamp());
ret.setPublicKeyMod(keys.getPublicKeyMod());
ret.setPublicKeyExp(keys.getPublicKeyExp());
ret.setValid(true);
return ret;
}
public Optional<SignatureEntity> getSignature(final String keyTimestamp) {
final Mapper<SignatureEntity> signatureEntityMapper
= tenantAwareCassandraMapperProvider.getMapper(SignatureEntity.class);
final Optional<SignatureEntity> ret = Optional.ofNullable(signatureEntityMapper.get(keyTimestamp));
return ret.filter(SignatureEntity::getValid);
}
/**
* @return the most current valid private key pair with key timestamp. If there are no valid key pairs, returns Optional.empty.
*/
public Optional<PrivateSignatureEntity> getPrivateSignature()
{
try {
final Optional<String> maximumKeyTimestamp = streamValidKeyTimestamps().max(String::compareTo);
return maximumKeyTimestamp.flatMap(this::getPrivateSignatureEntity);
}
catch (final InvalidQueryException e) {
return Optional.empty();
}
}
private Optional<PrivateSignatureEntity> getPrivateSignatureEntity(final String keyTimestamp) {
final Mapper<PrivateSignatureEntity> privateSignatureEntityMapper
= tenantAwareCassandraMapperProvider.getMapper(PrivateSignatureEntity.class);
final Optional<PrivateSignatureEntity> ret = Optional.ofNullable(privateSignatureEntityMapper.get(keyTimestamp));
return ret.filter(PrivateSignatureEntity::getValid);
}
public List<String> getAllKeyTimestamps() {
return streamValidKeyTimestamps().collect(Collectors.toList());
}
private Stream<String> streamValidKeyTimestamps() {
try {
final Select.Where selectValid = QueryBuilder.select(KEY_TIMESTAMP_COLUMN)
.from(TABLE_NAME)
.where(QueryBuilder.eq(VALID_COLUMN, true));
final ResultSet result = cassandraSessionProvider.getTenantSession().execute(selectValid);
return StreamSupport.stream(result.spliterator(), false)
.map(x -> x.get(KEY_TIMESTAMP_COLUMN, String.class));
}
catch (final InvalidQueryException e) {
return Stream.empty();
}
}
public void invalidateEntry(final String keyTimestamp) {
final Update.Assignments updateQuery = QueryBuilder.update(TABLE_NAME).where(QueryBuilder.eq(KEY_TIMESTAMP_COLUMN, keyTimestamp)).with(QueryBuilder.set(VALID_COLUMN, false));
cassandraSessionProvider.getTenantSession().execute(updateQuery);
}
}