| | |
| | | information: "Portions Copyright [year] [name of copyright owner]". |
| | | |
| | | Copyright 2007-2010 Sun Microsystems, Inc. |
| | | Portions copyright 2011-2015 ForgeRock AS. |
| | | Portions copyright 2011-2016 ForgeRock AS. |
| | | ! --> |
| | | <adm:managed-object name="replication-server" |
| | | plural-name="replication-servers" |
| | |
| | | </ldap:attribute> |
| | | </adm:profile> |
| | | </adm:property> |
| | | <adm:property name="cipher-transformation"> |
| | | <adm:synopsis> |
| | | Specifies the cipher for the directory server. |
| | | The syntax is "algorithm/mode/padding". |
| | | </adm:synopsis> |
| | | <adm:description> |
| | | The full transformation is required: specifying only an algorithm |
| | | and allowing the cipher provider to supply the default mode and |
| | | padding is not supported, because there is no guarantee these |
| | | default values are the same among different implementations. |
| | | Some cipher algorithms, including RC4 and ARCFOUR, do not have a |
| | | mode or padding, and hence must be specified using NONE for the |
| | | mode field and NoPadding for the padding field. For example, |
| | | RC4/NONE/NoPadding. |
| | | </adm:description> |
| | | <adm:requires-admin-action> |
| | | <adm:none> |
| | | <adm:synopsis> |
| | | Changes to this property take effect immediately but |
| | | only affect cryptographic operations performed after the |
| | | change. |
| | | </adm:synopsis> |
| | | </adm:none> |
| | | </adm:requires-admin-action> |
| | | <adm:default-behavior> |
| | | <adm:defined> |
| | | <adm:value>AES/CBC/PKCS5Padding</adm:value> |
| | | </adm:defined> |
| | | </adm:default-behavior> |
| | | <adm:syntax> |
| | | <adm:string /> |
| | | </adm:syntax> |
| | | <adm:profile name="ldap"> |
| | | <ldap:attribute> |
| | | <ldap:name>ds-cfg-cipher-transformation</ldap:name> |
| | | </ldap:attribute> |
| | | </adm:profile> |
| | | </adm:property> |
| | | <adm:property name="cipher-key-length"> |
| | | <adm:synopsis> |
| | | Specifies the key length in bits for the preferred cipher. |
| | | </adm:synopsis> |
| | | <adm:requires-admin-action> |
| | | <adm:none> |
| | | <adm:synopsis> |
| | | Changes to this property take effect immediately but |
| | | only affect cryptographic operations performed after the |
| | | change. |
| | | </adm:synopsis> |
| | | </adm:none> |
| | | </adm:requires-admin-action> |
| | | <adm:default-behavior> |
| | | <adm:defined> |
| | | <adm:value>128</adm:value> |
| | | </adm:defined> |
| | | </adm:default-behavior> |
| | | <adm:syntax> |
| | | <adm:integer /> |
| | | </adm:syntax> |
| | | <adm:profile name="ldap"> |
| | | <ldap:attribute> |
| | | <ldap:name>ds-cfg-cipher-key-length</ldap:name> |
| | | </ldap:attribute> |
| | | </adm:profile> |
| | | </adm:property> |
| | | <adm:property name="confidentiality-enabled"> |
| | | <adm:synopsis> |
| | | Indicates whether the replication change-log should make records readable only by Directory Server. |
| | | Throughput and disk space are affected by the more expensive operations taking place. |
| | | </adm:synopsis> |
| | | <adm:description> |
| | | Confidentiality is achieved by encrypting records on all domains managed by this replication server. |
| | | Encrypting the records prevents unauthorized parties from accessing contents of LDAP operations. |
| | | For complete protection, consider enabling secure communications between servers. |
| | | Change number indexing is not affected by the setting. |
| | | </adm:description> |
| | | <adm:requires-admin-action> |
| | | <adm:none> |
| | | <adm:synopsis> |
| | | Changes to this property take effect immediately but |
| | | only affect operations performed after the change. |
| | | </adm:synopsis> |
| | | </adm:none> |
| | | </adm:requires-admin-action> |
| | | <adm:default-behavior> |
| | | <adm:defined> |
| | | <adm:value>false</adm:value> |
| | | </adm:defined> |
| | | </adm:default-behavior> |
| | | <adm:syntax> |
| | | <adm:boolean/> |
| | | </adm:syntax> |
| | | <adm:profile name="ldap"> |
| | | <ldap:attribute> |
| | | <ldap:name>ds-cfg-confidentiality-enabled</ldap:name> |
| | | </ldap:attribute> |
| | | </adm:profile> |
| | | </adm:property> |
| | | </adm:managed-object> |
| | |
| | | ds-cfg-weight $ |
| | | ds-cfg-monitoring-period $ |
| | | ds-cfg-compute-change-number $ |
| | | ds-cfg-source-address ) |
| | | ds-cfg-source-address $ |
| | | ds-cfg-cipher-transformation $ |
| | | ds-cfg-cipher-key-length $ |
| | | ds-cfg-confidentiality-enabled) |
| | | X-ORIGIN 'OpenDS Directory Server' ) |
| | | objectClasses: ( 1.3.6.1.4.1.26027.1.2.65 |
| | | NAME 'ds-backup-directory' |
| | |
| | | private MatchingRuleIndex(EntryContainer entryContainer, AttributeType attributeType, State state, Indexer indexer, |
| | | int indexEntryLimit, boolean encryptValues, CryptoSuite cryptoSuite) |
| | | { |
| | | super(getIndexName(entryContainer, attributeType, indexer.getIndexID()), state, indexEntryLimit, entryContainer); |
| | | super(getIndexName(entryContainer, attributeType, indexer.getIndexID()), state, indexEntryLimit, entryContainer, |
| | | cryptoSuite); |
| | | this.attributeType = attributeType; |
| | | this.indexer = indexer; |
| | | this.encryptValues = encryptValues; |
| | | this.cryptoSuite = cryptoSuite; |
| | | } |
| | | |
| | | Set<ByteString> indexEntry(Entry entry) |
| | |
| | | private int indexEntryLimit; |
| | | |
| | | private EntryIDSetCodec codec; |
| | | protected boolean encryptValues; |
| | | protected CryptoSuite cryptoSuite; |
| | | |
| | | /** |
| | |
| | | * The configured limit on the number of entry IDs that may be indexed by one key. |
| | | * @param entryContainer |
| | | * The entryContainer holding this index. |
| | | * @param cryptoSuite |
| | | * @throws StorageRuntimeException |
| | | * If an error occurs in the storage. |
| | | */ |
| | | DefaultIndex(TreeName name, State state, int indexEntryLimit, EntryContainer entryContainer) |
| | | DefaultIndex(TreeName name, State state, int indexEntryLimit, EntryContainer entryContainer, CryptoSuite cryptoSuite) |
| | | throws StorageRuntimeException |
| | | { |
| | | super(name); |
| | | this.indexEntryLimit = indexEntryLimit; |
| | | this.state = state; |
| | | this.entryContainer = entryContainer; |
| | | this.cryptoSuite = cryptoSuite; |
| | | } |
| | | |
| | | @Override |
| | |
| | | { |
| | | final EnumSet<IndexFlag> flags = state.getIndexFlags(txn, getName()); |
| | | codec = flags.contains(COMPACTED) ? CODEC_V2 : CODEC_V1; |
| | | if (encryptValues) |
| | | if (cryptoSuite.isEncrypted()) |
| | | { |
| | | codec = new EntryIDSet.EntryIDSetCodecV3(codec, cryptoSuite); |
| | | } |
| | |
| | | // Keeps temporary values during import encrypted even in on-disk buffers. |
| | | long importDecodeValue(ByteString value) |
| | | { |
| | | return encryptValues ? decodeValue(ByteString.empty(), value).iterator().next().longValue() : value.toLong(); |
| | | return cryptoSuite.isEncrypted() |
| | | ? decodeValue(ByteString.empty(), value).iterator().next().longValue() |
| | | : value.toLong(); |
| | | } |
| | | |
| | | ByteString importToValue(EntryID entryID) |
| | | { |
| | | return encryptValues ? toValue(newDefinedSet(entryID.longValue())) : entryID.toByteString(); |
| | | return cryptoSuite.isEncrypted() ? toValue(newDefinedSet(entryID.longValue())) : entryID.toByteString(); |
| | | } |
| | | |
| | | @Override |
| | |
| | | @Override |
| | | public boolean setConfidential(boolean indexConfidential) |
| | | { |
| | | final boolean rebuildRequired = !this.encryptValues && indexConfidential; |
| | | this.encryptValues = indexConfidential; |
| | | return rebuildRequired; |
| | | return cryptoSuite.isEncrypted() != indexConfidential; |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | /** The set of attribute indexes. */ |
| | | private final Map<AttributeType, AttributeIndex> attrIndexMap = new HashMap<>(); |
| | | |
| | | private final Map<AttributeType, CryptoSuite> attrCryptoMap = new HashMap<>(); |
| | | /** The set of VLV (Virtual List View) indexes. */ |
| | | private final Map<String, VLVIndex> vlvIndexMap = new HashMap<>(); |
| | | |
| | |
| | | |
| | | private final ServerContext serverContext; |
| | | |
| | | private CryptoSuite cryptoSuite; |
| | | |
| | | /** |
| | | * This class is responsible for managing the configuration for attribute |
| | | * indexes used within this entry container. |
| | |
| | | { |
| | | try |
| | | { |
| | | newAttributeIndex(cfg); |
| | | newAttributeIndex(cfg, null); |
| | | return true; |
| | | } |
| | | catch(Exception e) |
| | |
| | | final ConfigChangeResult ccr = new ConfigChangeResult(); |
| | | try |
| | | { |
| | | final AttributeIndex index = newAttributeIndex(cfg); |
| | | final CryptoSuite cryptoSuite = newCryptoSuite(cfg.isConfidentialityEnabled()); |
| | | final AttributeIndex index = newAttributeIndex(cfg, cryptoSuite); |
| | | storage.write(new WriteOperation() |
| | | { |
| | | @Override |
| | |
| | | ccr.addMessage(NOTE_INDEX_ADD_REQUIRES_REBUILD.get(cfg.getAttribute().getNameOrOID())); |
| | | } |
| | | attrIndexMap.put(cfg.getAttribute(), index); |
| | | attrCryptoMap.put(cfg.getAttribute(), cryptoSuite); |
| | | } |
| | | }); |
| | | } |
| | |
| | | public void run(WriteableTransaction txn) throws Exception |
| | | { |
| | | attrIndexMap.remove(cfg.getAttribute()).closeAndDelete(txn); |
| | | attrCryptoMap.remove(cfg.getAttribute()); |
| | | } |
| | | }); |
| | | } |
| | |
| | | config.addBackendVLVIndexDeleteListener(vlvIndexCfgManager); |
| | | } |
| | | |
| | | private AttributeIndex newAttributeIndex(BackendIndexCfg cfg) throws ConfigException |
| | | private CryptoSuite newCryptoSuite(boolean confidentiality) |
| | | { |
| | | return serverContext.getCryptoManager().newCryptoSuite(config.getCipherTransformation(), |
| | | config.getCipherKeyLength(), confidentiality); |
| | | } |
| | | |
| | | private AttributeIndex newAttributeIndex(BackendIndexCfg cfg, CryptoSuite cryptoSuite) throws ConfigException |
| | | { |
| | | return new AttributeIndex(cfg, state, this, cryptoSuite); |
| | | } |
| | |
| | | .compress(config.isEntriesCompressed()) |
| | | .encode(config.isCompactEncoding()) |
| | | .encrypt(config.isConfidentialityEnabled()) |
| | | .cryptoSuite(cryptoSuite) |
| | | .cryptoSuite(serverContext.getCryptoManager().newCryptoSuite(config.getCipherTransformation(), |
| | | config.getCipherKeyLength(),config.isConfidentialityEnabled())) |
| | | .schema(rootContainer.getCompressedSchema()) |
| | | .build(); |
| | | } |
| | |
| | | boolean shouldCreate = accessMode.isWriteable(); |
| | | try |
| | | { |
| | | cryptoSuite = serverContext.getCryptoManager().newCryptoSuite(config.getCipherTransformation(), |
| | | config.getCipherKeyLength()); |
| | | id2entry = new ID2Entry(getIndexName(ID2ENTRY_TREE_NAME), newDataConfig(config)); |
| | | id2entry.open(txn, shouldCreate); |
| | | id2childrenCount.open(txn, shouldCreate); |
| | |
| | | { |
| | | BackendIndexCfg indexCfg = config.getBackendIndex(idx); |
| | | |
| | | final AttributeIndex index = newAttributeIndex(indexCfg); |
| | | CryptoSuite cryptoSuite = newCryptoSuite(indexCfg.isConfidentialityEnabled()); |
| | | final AttributeIndex index = newAttributeIndex(indexCfg, cryptoSuite); |
| | | index.open(txn, shouldCreate); |
| | | if(!index.isTrusted()) |
| | | { |
| | | logger.info(NOTE_INDEX_ADD_REQUIRES_REBUILD, index.getName()); |
| | | } |
| | | attrIndexMap.put(indexCfg.getAttribute(), index); |
| | | attrCryptoMap.put(indexCfg.getAttribute(), cryptoSuite); |
| | | } |
| | | |
| | | for (String idx : config.listBackendVLVIndexes()) |
| | |
| | | @Override |
| | | public void run(WriteableTransaction txn) throws Exception |
| | | { |
| | | cryptoSuite.setCipherTransformation(cfg.getCipherTransformation()); |
| | | cryptoSuite.setCipherKeyLength(cfg.getCipherKeyLength()); |
| | | id2entry.setDataConfig(newDataConfig(cfg)); |
| | | |
| | | EntryContainer.this.config = cfg; |
| | | } |
| | | }); |
| | | for (CryptoSuite indexCrypto : attrCryptoMap.values()) |
| | | { |
| | | indexCrypto.newParameters(cfg.getCipherTransformation(), cfg.getCipherKeyLength(), indexCrypto.isEncrypted()); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | } |
| | | |
| | | @Override |
| | | public CryptoSuite newCryptoSuite(String cipherTransformation, int cipherKeyLength) |
| | | public CryptoSuite newCryptoSuite(String cipherTransformation, int cipherKeyLength, boolean encrypt) |
| | | { |
| | | return new CryptoSuite(this, cipherTransformation, cipherKeyLength); |
| | | return new CryptoSuite(this, cipherTransformation, cipherKeyLength, encrypt); |
| | | } |
| | | } |
| | |
| | | */ |
| | | package org.opends.server.crypto; |
| | | |
| | | import net.jcip.annotations.Immutable; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | |
| | | /** Defines cipher transformation and hash algorithm for cryptographic related operations. */ |
| | | public class CryptoSuite |
| | | { |
| | | private String cipherTransformation; |
| | | private int cipherKeyLength; |
| | | /** Cipher specific settings that can change at runtime. */ |
| | | @Immutable |
| | | private static final class CipherInfo |
| | | { |
| | | private final String cipherTransformation; |
| | | private final int cipherKeyLength; |
| | | private final boolean encrypt; |
| | | |
| | | CipherInfo(String cipherTransformation, int cipherKeyLength, boolean encrypt) |
| | | { |
| | | this.cipherTransformation = cipherTransformation; |
| | | this.cipherKeyLength = cipherKeyLength; |
| | | this.encrypt = encrypt; |
| | | } |
| | | } |
| | | |
| | | private volatile CipherInfo cipherInfo; |
| | | private final CryptoManager cryptoManager; |
| | | |
| | | /** |
| | |
| | | * @param cryptoManager the CryptoManager to use for cryptographic operations |
| | | * @param cipherTransformation the initial cipher transformation |
| | | * @param cipherKeyLength the initial key length for the cipher |
| | | * @param encrypt if the user of the crypto suite needs encryption |
| | | */ |
| | | public CryptoSuite(CryptoManager cryptoManager, String cipherTransformation, int cipherKeyLength) |
| | | public CryptoSuite(CryptoManager cryptoManager, String cipherTransformation, int cipherKeyLength, boolean encrypt) |
| | | { |
| | | this.cryptoManager = cryptoManager; |
| | | this.cipherTransformation = cipherTransformation; |
| | | this.cipherKeyLength = cipherKeyLength; |
| | | this.cipherInfo = new CipherInfo(cipherTransformation, cipherKeyLength, encrypt); |
| | | } |
| | | |
| | | /** |
| | | * Returns the cipher transformation to use. |
| | | * |
| | | * @return the cipher transformation to use |
| | | */ |
| | | public String getCipherTransformation() |
| | | { |
| | | return cipherTransformation; |
| | | } |
| | | |
| | | /** |
| | | * Returns the cipher key length to use. |
| | | * |
| | | * @return the cipher key length to use |
| | | */ |
| | | public int getCipherKeyLength() |
| | | { |
| | | return cipherKeyLength; |
| | | } |
| | | |
| | | /** |
| | | * Sets the cipher transformation for the CryptoSuite. |
| | | * Set new cipher and enable parameters for the crypto suite. |
| | | * |
| | | * @param cipherTransformation the new cipher transformation |
| | | */ |
| | | public void setCipherTransformation(String cipherTransformation) |
| | | { |
| | | this.cipherTransformation = cipherTransformation; |
| | | } |
| | | |
| | | /** |
| | | * Sets the key length for the CryptoSuite. |
| | | * |
| | | * @param cipherKeyLength the new key length |
| | | * @param enabled true if the user of the crypto suite needs encryption |
| | | */ |
| | | public void setCipherKeyLength(int cipherKeyLength) |
| | | public void newParameters(String cipherTransformation, int cipherKeyLength, boolean enabled) |
| | | { |
| | | this.cipherKeyLength = cipherKeyLength; |
| | | cipherInfo = new CipherInfo(cipherTransformation, cipherKeyLength, enabled); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public byte[] encrypt(byte[] data) throws GeneralSecurityException, CryptoManagerException |
| | | { |
| | | return cryptoManager.encrypt(cipherTransformation, cipherKeyLength, data); |
| | | CipherInfo currentCipher = cipherInfo; |
| | | return cryptoManager.encrypt(currentCipher.cipherTransformation, currentCipher.cipherKeyLength, data); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public CipherOutputStream getCipherOutputStream(OutputStream os) throws CryptoManagerException |
| | | { |
| | | return cryptoManager.getCipherOutputStream(cipherTransformation, cipherKeyLength, os); |
| | | CipherInfo currentCipher = cipherInfo; |
| | | return cryptoManager.getCipherOutputStream(currentCipher.cipherTransformation, currentCipher.cipherKeyLength, os); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns whether the user of the crypto suite needs encryption. |
| | | * |
| | | * @return true if the user of the crypto suite needs encryption |
| | | */ |
| | | public boolean isEncrypted() |
| | | { |
| | | return cipherInfo.encrypt; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() |
| | | { |
| | | StringBuilder builder = new StringBuilder(); |
| | | CipherInfo currentCipher = cipherInfo; |
| | | builder.append("CryptoSuite(cipherTransformation="); |
| | | builder.append(cipherTransformation); |
| | | builder.append(currentCipher.cipherTransformation); |
| | | builder.append(", keyLength="); |
| | | builder.append(cipherKeyLength); |
| | | builder.append(currentCipher.cipherKeyLength); |
| | | builder.append(", encrypt="); |
| | | builder.append(currentCipher.encrypt); |
| | | builder.append(")"); |
| | | return builder.toString(); |
| | | } |
| | |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2013-2014 ForgeRock AS. |
| | | * Portions copyright 2013-2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.replication.protocol; |
| | | |
| | |
| | | */ |
| | | public abstract class ReplicationMsg |
| | | { |
| | | /** Reserved type for uses other than protocol messages. */ |
| | | public static final byte MSG_TYPE_DISK_ENCODING = -1; |
| | | |
| | | // PDU type values kept for compatibility with replication protocol version 1 |
| | | static final byte MSG_TYPE_MODIFY_V1 = 1; |
| | | static final byte MSG_TYPE_ADD_V1 = 2; |
| | |
| | | import org.opends.server.api.VirtualAttributeProvider; |
| | | import org.opends.server.backends.ChangelogBackend; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.crypto.CryptoSuite; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.MultiDomainServerState; |
| | | import org.opends.server.replication.common.ServerState; |
| | |
| | | */ |
| | | private static final List<ReplicationServer> allInstances = new ArrayList<>(); |
| | | |
| | | private final CryptoSuite cryptoSuite; |
| | | |
| | | /** |
| | | * Creates a new Replication server using the provided configuration entry. |
| | | * |
| | |
| | | this.domainPredicate = predicate; |
| | | |
| | | enableExternalChangeLog(); |
| | | this.changelogDB = new FileChangelogDB(this, config.getReplicationDBDirectory()); |
| | | cryptoSuite = DirectoryServer.getInstance().getServerContext().getCryptoManager(). |
| | | newCryptoSuite(cfg.getCipherTransformation(), cfg.getCipherKeyLength(), cfg.isConfidentialityEnabled()); |
| | | |
| | | this.changelogDB = new FileChangelogDB(this, config.getReplicationDBDirectory(), cryptoSuite); |
| | | |
| | | replSessionSecurity = new ReplSessionSecurity(); |
| | | initialize(); |
| | |
| | | } |
| | | } |
| | | |
| | | cryptoSuite.newParameters(config.getCipherTransformation(), config.getCipherKeyLength(), |
| | | config.isConfidentialityEnabled()); |
| | | |
| | | // changing the listen port requires to stop the listen thread |
| | | // and restart it. |
| | | if (getReplicationPort() != oldConfig.getReplicationPort()) |
| | |
| | | return MultimasterReplication.isECLEnabled(); |
| | | } |
| | | |
| | | /** |
| | | * Return whether change-log records should be encrypted. |
| | | * @return trus if change-log records should be encrypted |
| | | */ |
| | | public boolean isEncrypted() |
| | | { |
| | | return config.isConfidentialityEnabled(); |
| | | } |
| | | |
| | | @Override |
| | | public String toString() |
| | | { |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2014-2015 ForgeRock AS. |
| | | * Copyright 2014-2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.replication.server.changelog.file; |
| | | |
| | |
| | | write(parser.encodeRecord(record)); |
| | | writer.flush(); |
| | | } |
| | | catch (IOException e) |
| | | catch (Exception e) |
| | | { |
| | | throw new ChangelogException(ERR_CHANGELOG_UNABLE_TO_ADD_RECORD.get(record.toString(), |
| | | writer.getFile().getPath()), e); |
| | |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | import java.util.concurrent.atomic.AtomicLong; |
| | | import java.util.concurrent.locks.ReentrantReadWriteLock; |
| | |
| | | private static final byte STRING_SEPARATOR = 0; |
| | | |
| | | @Override |
| | | public ByteString encodeRecord(final Record<Long, ChangeNumberIndexRecord> record) |
| | | public ByteString encodeRecord(final Record<Long, ChangeNumberIndexRecord> record) throws IOException |
| | | { |
| | | final ChangeNumberIndexRecord cnIndexRecord = record.getValue(); |
| | | return new ByteStringBuilder() |
| | |
| | | import org.forgerock.util.time.TimeService; |
| | | import org.opends.server.api.DirectoryThread; |
| | | import org.opends.server.backends.ChangelogBackend; |
| | | import org.opends.server.crypto.CryptoSuite; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.MultiDomainServerState; |
| | | import org.opends.server.replication.common.ServerState; |
| | |
| | | private static final DBCursor<UpdateMsg> EMPTY_CURSOR_REPLICA_DB = |
| | | new FileReplicaDBCursor(EMPTY_CURSOR, null, AFTER_MATCHING_KEY); |
| | | |
| | | private final CryptoSuite cryptoSuite; |
| | | /** |
| | | * Creates a new changelog DB. |
| | | * |
| | |
| | | * the local replication server. |
| | | * @param dbDirectoryPath |
| | | * the path where the changelog files reside. |
| | | * @param cryptoSuite |
| | | * the cryptosuite to use for encryption |
| | | * @throws ConfigException |
| | | * if a problem occurs opening the supplied directory |
| | | */ |
| | | public FileChangelogDB(final ReplicationServer replicationServer, String dbDirectoryPath) |
| | | public FileChangelogDB(final ReplicationServer replicationServer, String dbDirectoryPath, CryptoSuite cryptoSuite) |
| | | throws ConfigException |
| | | { |
| | | this.replicationServer = replicationServer; |
| | | this.dbDirectory = makeDir(dbDirectoryPath); |
| | | this.cryptoSuite = cryptoSuite; |
| | | } |
| | | |
| | | private File makeDir(final String dbDirName) throws ConfigException |
| | |
| | | return null; |
| | | } |
| | | |
| | | final FileReplicaDB newDB = new FileReplicaDB(serverId, baseDN, server, replicationEnv); |
| | | final FileReplicaDB newDB = new FileReplicaDB(serverId, baseDN, server, cryptoSuite, replicationEnv); |
| | | domainMap.put(serverId, newDB); |
| | | return Pair.of(newDB, true); |
| | | } |
| | |
| | | package org.opends.server.replication.server.changelog.file; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | import static org.opends.server.replication.protocol.ProtocolVersion.REPLICATION_PROTOCOL_V7; |
| | | |
| | | import java.io.IOException; |
| | | import java.security.GeneralSecurityException; |
| | | import java.util.Date; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | |
| | |
| | | |
| | | import org.forgerock.opendj.config.server.ConfigException; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ByteStringBuilder; |
| | | import org.opends.server.api.MonitorData; |
| | | import org.forgerock.opendj.server.config.server.MonitorProviderCfg; |
| | | import org.opends.server.api.MonitorProvider; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.crypto.CryptoSuite; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.ProtocolVersion; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | import org.opends.server.replication.server.ReplicationServer; |
| | | import org.opends.server.replication.server.ReplicationServerDomain; |
| | |
| | | import org.opends.server.replication.server.changelog.api.DBCursor.PositionStrategy; |
| | | import org.opends.server.replication.server.changelog.file.Log.RepositionableCursor; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.opends.server.types.CryptoManagerException; |
| | | import org.opends.server.types.InitializationException; |
| | | |
| | | /** |
| | |
| | | */ |
| | | class FileReplicaDB |
| | | { |
| | | |
| | | /** The parser of records stored in Replica DB. */ |
| | | static final RecordParser<CSN, UpdateMsg> RECORD_PARSER = new ReplicaDBParser(); |
| | | |
| | | /** Class that allows atomically setting oldest and newest CSNs without synchronization. */ |
| | | @Immutable |
| | | private static final class CSNLimits |
| | |
| | | * If a database problem happened |
| | | */ |
| | | FileReplicaDB(final int serverId, final DN baseDN, final ReplicationServer replicationServer, |
| | | final ReplicationEnvironment replicationEnv) throws ChangelogException |
| | | final CryptoSuite cryptoSuite, final ReplicationEnvironment replicationEnv) throws ChangelogException |
| | | { |
| | | this.serverId = serverId; |
| | | this.baseDN = baseDN; |
| | | this.replicationServer = replicationServer; |
| | | this.replicationEnv = replicationEnv; |
| | | this.log = createLog(replicationEnv); |
| | | this.log = createLog(replicationEnv, cryptoSuite); |
| | | this.csnLimits = new CSNLimits(readOldestCSN(), readNewestCSN()); |
| | | |
| | | DirectoryServer.deregisterMonitorProvider(dbMonitor); |
| | |
| | | return record == null ? null : record.getKey(); |
| | | } |
| | | |
| | | private Log<CSN, UpdateMsg> createLog(final ReplicationEnvironment replicationEnv) throws ChangelogException |
| | | private Log<CSN, UpdateMsg> createLog(final ReplicationEnvironment replicationEnv, final CryptoSuite cryptoSuite) |
| | | throws ChangelogException |
| | | { |
| | | final ReplicationServerDomain domain = replicationServer.getReplicationServerDomain(baseDN, true); |
| | | return replicationEnv.getOrCreateReplicaDB(baseDN, serverId, domain.getGenerationId()); |
| | | return replicationEnv.getOrCreateReplicaDB(baseDN, serverId, domain.getGenerationId(), cryptoSuite); |
| | | } |
| | | |
| | | /** |
| | |
| | | log.dumpAsTextFile(log.getPath()); |
| | | } |
| | | |
| | | static ReplicaDBParser newReplicaDBParser(final CryptoSuite cryptoSuite) |
| | | { |
| | | return new ReplicaDBParser(cryptoSuite); |
| | | } |
| | | |
| | | /** Parser of records persisted in the ReplicaDB log. */ |
| | | private static class ReplicaDBParser implements RecordParser<CSN, UpdateMsg> |
| | | { |
| | | private static final byte RECORD_VERSION = 0x01; |
| | | private final CryptoSuite cryptoSuite; |
| | | /** Adjusts the ByteStringBuilder capacity to avoid capacity increases (and copies) when encoding records. */ |
| | | private int encryptionOverhead; |
| | | |
| | | ReplicaDBParser(CryptoSuite cryptoSuite) |
| | | { |
| | | this.cryptoSuite = cryptoSuite; |
| | | } |
| | | |
| | | @Override |
| | | public ByteString encodeRecord(final Record<CSN, UpdateMsg> record) |
| | | public ByteString encodeRecord(final Record<CSN, UpdateMsg> record) throws IOException |
| | | { |
| | | final UpdateMsg message = record.getValue(); |
| | | if (cryptoSuite.isEncrypted()) |
| | | { |
| | | try |
| | | { |
| | | byte[] messageBytes = message.getBytes(); |
| | | ByteStringBuilder builder = new ByteStringBuilder(messageBytes.length + encryptionOverhead); |
| | | builder.appendByte(UpdateMsg.MSG_TYPE_DISK_ENCODING); |
| | | builder.appendByte(RECORD_VERSION); |
| | | builder.appendBytes(cryptoSuite.encrypt(messageBytes)); |
| | | final int overhead = builder.length() - messageBytes.length; |
| | | if (encryptionOverhead < overhead) |
| | | { |
| | | encryptionOverhead = overhead; |
| | | } |
| | | return builder.toByteString(); |
| | | } |
| | | catch (GeneralSecurityException | CryptoManagerException e) |
| | | { |
| | | throw new IOException(e); |
| | | } |
| | | } |
| | | return ByteString.wrap(message.getBytes()); |
| | | } |
| | | |
| | |
| | | { |
| | | try |
| | | { |
| | | final UpdateMsg msg = |
| | | (UpdateMsg) UpdateMsg.generateMsg(data.toByteArray(), ProtocolVersion.REPLICATION_PROTOCOL_V7); |
| | | byte[] recordBytes; |
| | | if (data.byteAt(0) == UpdateMsg.MSG_TYPE_DISK_ENCODING) |
| | | { |
| | | final int version = data.byteAt(1); |
| | | if (version != RECORD_VERSION) |
| | | { |
| | | throw new DecodingException(ERR_UNRECOGNIZED_RECORD_VERSION.get(version)); |
| | | } |
| | | recordBytes = cryptoSuite.decrypt(data.subSequence(2, data.length()).toByteArray()); |
| | | } |
| | | else |
| | | { |
| | | recordBytes = data.toByteArray(); |
| | | } |
| | | final UpdateMsg msg = (UpdateMsg) UpdateMsg.generateMsg(recordBytes, REPLICATION_PROTOCOL_V7); |
| | | return Record.from(msg.getCSN(), msg); |
| | | } |
| | | catch (Exception e) |
| | |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public CSN decodeKeyFromString(String key) throws ChangelogException |
| | | { |
| | | return new CSN(key); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public String encodeKeyToString(CSN key) |
| | | { |
| | | return key.toString(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public CSN getMaxKey() |
| | | { |
| | | return CSN.MAX_CSN_VALUE; |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2014 ForgeRock AS. |
| | | * Copyright 2014-2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.replication.server.changelog.file; |
| | | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.opends.server.replication.server.changelog.api.ChangelogException; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * Parser of a log record. |
| | | * <p> |
| | |
| | | * The record to encode. |
| | | * @return the bytes array representing the (key,value) record |
| | | */ |
| | | ByteString encodeRecord(Record<K, V> record); |
| | | ByteString encodeRecord(Record<K, V> record) throws IOException; |
| | | |
| | | /** |
| | | * Read the key from the provided string. |
| | |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.util.time.TimeService; |
| | | import org.opends.server.crypto.CryptoSuite; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | | import org.opends.server.replication.server.ChangelogState; |
| | |
| | | * @throws ChangelogException |
| | | * if an error occurs. |
| | | */ |
| | | Log<CSN, UpdateMsg> getOrCreateReplicaDB(final DN domainDN, final int serverId, final long generationId) |
| | | throws ChangelogException |
| | | Log<CSN, UpdateMsg> getOrCreateReplicaDB(final DN domainDN, final int serverId, final long generationId, |
| | | final CryptoSuite cryptoSuite) throws ChangelogException |
| | | { |
| | | if (logger.isTraceEnabled()) |
| | | { |
| | |
| | | ensureGenerationIdFileExists(generationIdPath); |
| | | changelogState.setDomainGenerationId(domainDN, generationId); |
| | | |
| | | return openLog(serverIdPath, FileReplicaDB.RECORD_PARSER, |
| | | return openLog(serverIdPath, FileReplicaDB.newReplicaDBParser(cryptoSuite), |
| | | new LogRotationParameters(REPLICA_DB_MAX_LOG_FILE_SIZE_IN_BYTES, 0, 0), logsReplicaDB); |
| | | } |
| | | } |
| | |
| | | * @return a new {@link CryptoSuite} for the cipher and key |
| | | * @param cipherTransformation cipher transformation string specification |
| | | * @param cipherKeyLength length of key in bits |
| | | * @param encrypt true if the user of the crypto suite needs encryption |
| | | */ |
| | | CryptoSuite newCryptoSuite(String cipherTransformation, int cipherKeyLength); |
| | | CryptoSuite newCryptoSuite(String cipherTransformation, int cipherKeyLength, boolean encrypt); |
| | | } |
| | |
| | | ERR_CHANGELOG_RESET_CHANGE_NUMBER_CSN_TOO_OLD_294=The change number could not be reset to %d because the associated \ |
| | | change with CSN '%s' has already been purged from the change log. Try resetting to a more recent change |
| | | ERR_REPLICATION_CHANGE_NUMBER_DISABLED_295=Change number indexing is disabled for replication domain '%s' |
| | | INFO_CHANGELOG_FILTER_OUT_RECORD_BREAKING_ORDER_296=Filtering out from log file '%s' the record '%s'\ |
| | | because it would break ordering. Last key appended is '%s'. |
| | | INFO_CHANGELOG_FILTER_OUT_RECORD_BREAKING_ORDER_296=Filtering out from log file '%s' the record '%s' \ |
| | | because it would break ordering. Last key appended is '%s'. |
| | | ERR_UNRECOGNIZED_RECORD_VERSION_297=Cannot decode change-log record with version %x |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2015 ForgeRock AS. |
| | | * Copyright 2015-2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.backends.pluggable; |
| | | |
| | |
| | | import org.opends.server.backends.pluggable.spi.TreeName; |
| | | import org.opends.server.backends.pluggable.spi.UpdateFunction; |
| | | import org.opends.server.backends.pluggable.spi.WriteableTransaction; |
| | | import org.opends.server.crypto.CryptoSuite; |
| | | import org.testng.annotations.BeforeMethod; |
| | | import org.testng.annotations.Test; |
| | | |
| | |
| | | { |
| | | final State state = mock(State.class); |
| | | when(state.getIndexFlags(any(ReadableTransaction.class), any(TreeName.class))).thenReturn(indexFlags); |
| | | return new DefaultIndex(new TreeName("dc=example,dc=com", name), state, indexLimit, mock(EntryContainer.class)); |
| | | final CryptoSuite cryptoSuite = mock(CryptoSuite.class); |
| | | when(cryptoSuite.isEncrypted()).thenReturn(false); |
| | | return new DefaultIndex(new TreeName("dc=example,dc=com", name), state, indexLimit, mock(EntryContainer.class), |
| | | cryptoSuite); |
| | | } |
| | | |
| | | static final class DummyWriteableTransaction implements WriteableTransaction { |
| | |
| | | import org.opends.server.backends.pluggable.spi.StorageRuntimeException; |
| | | import org.opends.server.backends.pluggable.spi.TreeName; |
| | | import org.opends.server.backends.pluggable.spi.WriteableTransaction; |
| | | import org.opends.server.crypto.CryptoSuite; |
| | | import org.testng.annotations.Test; |
| | | |
| | | import com.forgerock.opendj.util.PackedLong; |
| | |
| | | { |
| | | private static final State state; |
| | | private static final EntryContainer entryContainer; |
| | | private static final CryptoSuite cryptoSuite; |
| | | |
| | | static |
| | | { |
| | |
| | | state = Mockito.mock(State.class); |
| | | Mockito.when(state.getIndexFlags(Mockito.any(ReadableTransaction.class), Mockito.any(TreeName.class))).thenReturn( |
| | | EnumSet.of(State.IndexFlag.COMPACTED)); |
| | | |
| | | cryptoSuite = Mockito.mock(CryptoSuite.class); |
| | | Mockito.when(cryptoSuite.isEncrypted()).thenReturn(false); |
| | | }; |
| | | |
| | | DummyIndex(int indexEntryLimit) throws StorageRuntimeException |
| | | { |
| | | super(TreeName.valueOf("/dumy/dummy"), state, indexEntryLimit, entryContainer); |
| | | super(TreeName.valueOf("/dummy/dummy"), state, indexEntryLimit, entryContainer, cryptoSuite); |
| | | open(Mockito.mock(WriteableTransaction.class), false); |
| | | } |
| | | } |
| | |
| | | private int queueSize; |
| | | private int windowSize; |
| | | private SortedSet<String> servers; |
| | | private boolean confidentialityEnabled; |
| | | |
| | | /* |
| | | * Assured mode properties |
| | |
| | | } |
| | | |
| | | @Override |
| | | public boolean isConfidentialityEnabled() |
| | | { |
| | | return confidentialityEnabled; |
| | | } |
| | | |
| | | @Override |
| | | public long getAssuredTimeout() |
| | | { |
| | | return assuredTimeout; |
| | | } |
| | | |
| | | @Override |
| | | public int getCipherKeyLength() |
| | | { |
| | | return 128; |
| | | } |
| | | |
| | | @Override |
| | | public String getCipherTransformation() |
| | | { |
| | | return "AES/CBC/PKCS5Padding"; |
| | | } |
| | | |
| | | @Override |
| | | public int getDegradedStatusThreshold() |
| | | { |
| | | return degradedStatusThreshold; |
| | |
| | | { |
| | | this.computeChangenumber = computeChangenumber; |
| | | } |
| | | |
| | | public void setConfidentialityEnabled(boolean confidentialityEnabled) |
| | | { |
| | | this.confidentialityEnabled = confidentialityEnabled; |
| | | } |
| | | } |
| | |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2014-2015 ForgeRock AS. |
| | | * Copyright 2014-2016 ForgeRock AS. |
| | | */ |
| | | package org.opends.server.replication.server.changelog.file; |
| | | |
| | |
| | | |
| | | import java.io.File; |
| | | import java.io.FileNotFoundException; |
| | | import java.io.IOException; |
| | | import java.io.RandomAccessFile; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public ByteString encodeRecord(Record<Integer, Integer> record) |
| | | public ByteString encodeRecord(Record<Integer, Integer> record) throws IOException |
| | | { |
| | | return new ByteStringBuilder().appendInt(record.getKey()).appendInt(record.getValue()).toByteString(); |
| | | } |
| | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.util.time.TimeService; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.crypto.CryptoSuite; |
| | | import org.opends.server.replication.ReplicationTestCase; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | |
| | | public class FileReplicaDBTest extends ReplicationTestCase |
| | | { |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | private final String cipherTransformation = "AES/CBC/PKCS5Padding"; |
| | | private final int keyLength = 128; |
| | | private DN TEST_ROOT_DN; |
| | | |
| | | /** |
| | |
| | | { |
| | | CSN[] csns = generateCSNs(1, 0, 2); |
| | | return new Object[][] { |
| | | { new DeleteMsg(TEST_ROOT_DN, csns[0], "uid") }, |
| | | { new DeleteMsg(TEST_ROOT_DN, csns[1], "uid") }, |
| | | { new DeleteMsg(TEST_ROOT_DN, csns[0], "uid"), false }, |
| | | { new DeleteMsg(TEST_ROOT_DN, csns[1], "uid"), false }, |
| | | { new DeleteMsg(TEST_ROOT_DN, csns[0], "uid"), true }, |
| | | { new DeleteMsg(TEST_ROOT_DN, csns[1], "uid"), true }, |
| | | }; |
| | | } |
| | | |
| | | @Test(dataProvider="messages") |
| | | public void testRecordParser(UpdateMsg msg) throws Exception |
| | | public void testRecordParser(UpdateMsg msg, boolean confidential) throws Exception |
| | | { |
| | | RecordParser<CSN, UpdateMsg> parser = FileReplicaDB.RECORD_PARSER; |
| | | |
| | | RecordParser<CSN, UpdateMsg> parser = FileReplicaDB.newReplicaDBParser(createCryptoSuite(confidential)); |
| | | ByteString data = parser.encodeRecord(Record.from(msg.getCSN(), msg)); |
| | | Record<CSN, UpdateMsg> record = parser.decodeRecord(data); |
| | | |
| | |
| | | assertThat(record.getValue()).isEqualTo(msg); |
| | | } |
| | | |
| | | @Test(dataProvider="messages") |
| | | public void testRecordEncodingWithAndWithoutConfidentiality(UpdateMsg msg, boolean confidential) throws Exception |
| | | { |
| | | CryptoSuite cryptoSuite = createCryptoSuite(confidential); |
| | | RecordParser<CSN, UpdateMsg> parser = FileReplicaDB.newReplicaDBParser(cryptoSuite); |
| | | |
| | | ByteString data1 = parser.encodeRecord(Record.from(msg.getCSN(), msg)); |
| | | cryptoSuite.newParameters(cipherTransformation, keyLength, !confidential); |
| | | ByteString data2 = parser.encodeRecord(Record.from(msg.getCSN(), msg)); |
| | | |
| | | assertFalse(data1.equals(data2)); |
| | | } |
| | | |
| | | private CryptoSuite createCryptoSuite(boolean confidential) |
| | | { |
| | | return DirectoryServer.getCryptoManager().newCryptoSuite(cipherTransformation, keyLength, confidential); |
| | | } |
| | | @Test |
| | | public void testDomainDNWithForwardSlashes() throws Exception |
| | | { |
| | |
| | | |
| | | testRoot = createCleanDir(); |
| | | dbEnv = new ReplicationEnvironment(testRoot.getPath(), replicationServer, TimeService.SYSTEM); |
| | | replicaDB = new FileReplicaDB(1, TEST_ROOT_DN, replicationServer, dbEnv); |
| | | replicaDB = new FileReplicaDB(1, TEST_ROOT_DN, replicationServer, createCryptoSuite(false), dbEnv); |
| | | |
| | | // Populate the db with 'max' msg |
| | | int mySeqnum = 1; |
| | |
| | | debugInfo(tn, "SHUTDOWN replicaDB and recreate"); |
| | | replicaDB.shutdown(); |
| | | |
| | | replicaDB = new FileReplicaDB(1, TEST_ROOT_DN, replicationServer, dbEnv); |
| | | replicaDB = new FileReplicaDB(1, TEST_ROOT_DN, replicationServer, createCryptoSuite(false), dbEnv); |
| | | assertLimits(replicaDB, csns[1], csns[max]); |
| | | |
| | | // Populate the db with 'max' msg |
| | |
| | | throws IOException, ConfigException |
| | | { |
| | | final int changelogPort = findFreePort(); |
| | | return new ReplicationServer( |
| | | new ReplServerFakeConfiguration(changelogPort, null, 0, 2, queueSize, windowSize, null)); |
| | | ReplServerFakeConfiguration replServerFakeCfg = |
| | | new ReplServerFakeConfiguration(changelogPort, null, 0, 2, queueSize, windowSize, null); |
| | | return new ReplicationServer(replServerFakeCfg); |
| | | } |
| | | |
| | | private FileReplicaDB newReplicaDB(ReplicationServer rs) throws Exception |
| | |
| | | package org.opends.server.replication.server.changelog.file; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.io.RandomAccessFile; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public ByteString encodeRecord(Record<String, String> record) |
| | | public ByteString encodeRecord(Record<String, String> record) throws IOException |
| | | { |
| | | return new ByteStringBuilder() |
| | | .appendUtf8(record.getKey()).appendByte(STRING_SEPARATOR) |
| | |
| | | import org.forgerock.util.time.TimeService; |
| | | import org.opends.server.DirectoryServerTestCase; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.crypto.CryptoSuite; |
| | | import org.opends.server.replication.common.CSN; |
| | | import org.opends.server.replication.common.CSNGenerator; |
| | | import org.opends.server.replication.protocol.UpdateMsg; |
| | |
| | | private static final String DN1_AS_STRING = "cn=test1,dc=company.com"; |
| | | private static final String DN2_AS_STRING = "cn=te::st2,dc=company.com"; |
| | | private static final String DN3_AS_STRING = "cn=test3,dc=company.com"; |
| | | |
| | | private static final String TEST_DIRECTORY_CHANGELOG = "test-output/changelog"; |
| | | |
| | | private static final CryptoSuite cryptoSuite = mock(CryptoSuite.class); |
| | | |
| | | @BeforeClass |
| | | public void setUp() throws Exception |
| | | { |
| | | // This test suite depends on having the schema available for DN decoding. |
| | | TestCaseUtils.startFakeServer(); |
| | | when(cryptoSuite.isEncrypted()).thenReturn(false); |
| | | } |
| | | |
| | | @AfterClass |
| | |
| | | final DN domainDN = DN.valueOf(DN1_AS_STRING); |
| | | ReplicationEnvironment environment = createReplicationEnv(rootPath); |
| | | cnDB = environment.getOrCreateCNIndexDB(); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); |
| | | replicaDB2 = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_2, 1); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite); |
| | | replicaDB2 = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_2, 1, cryptoSuite); |
| | | |
| | | final ChangelogState state = environment.readOnDiskChangelogState(); |
| | | |
| | |
| | | for (int j = 1; j <= 10; j++) |
| | | { |
| | | // 3 domains, 10 server id each, generation id is different for each domain |
| | | replicaDBs.add(environment.getOrCreateReplicaDB(domainDNs.get(i), j, i+1)); |
| | | replicaDBs.add(environment.getOrCreateReplicaDB(domainDNs.get(i), j, i+1, cryptoSuite)); |
| | | } |
| | | } |
| | | |
| | |
| | | final DN domainDN = DN.valueOf(DN1_AS_STRING); |
| | | ReplicationEnvironment environment = createReplicationEnv(rootPath); |
| | | cnDB = environment.getOrCreateCNIndexDB(); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite); |
| | | |
| | | // put server id 1 offline |
| | | CSN offlineCSN = new CSN(TimeThread.getTime(), 0, SERVER_ID_1); |
| | |
| | | final DN domainDN = DN.valueOf(DN1_AS_STRING); |
| | | ReplicationEnvironment environment = createReplicationEnv(rootPath); |
| | | cnDB = environment.getOrCreateCNIndexDB(); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite); |
| | | |
| | | File offlineStateFile = new File(environment.getServerIdPath("1", 1), REPLICA_OFFLINE_STATE_FILENAME); |
| | | offlineStateFile.createNewFile(); |
| | |
| | | |
| | | ReplicationEnvironment environment = createReplicationEnv(rootPath); |
| | | cnDB = environment.getOrCreateCNIndexDB(); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite); |
| | | |
| | | // put server id 1 offline twice |
| | | CSNGenerator csnGenerator = new CSNGenerator(SERVER_ID_1, 100); |
| | |
| | | |
| | | ReplicationEnvironment environment = createReplicationEnv(rootPath); |
| | | cnDB = environment.getOrCreateCNIndexDB(); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite); |
| | | |
| | | // put server id 1 offline |
| | | environment.notifyReplicaOffline(domainDN, new CSN(TimeThread.getTime(), 0, SERVER_ID_1)); |
| | |
| | | |
| | | ReplicationEnvironment environment = createReplicationEnv(rootPath); |
| | | cnDB = environment.getOrCreateCNIndexDB(); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite); |
| | | CSN offlineCSN = new CSN(TimeThread.getTime(), 0, SERVER_ID_1); |
| | | environment.notifyReplicaOffline(domainDN, offlineCSN); |
| | | |
| | |
| | | File rootPath = new File(TEST_DIRECTORY_CHANGELOG); |
| | | DN domainDN = DN.valueOf(DN1_AS_STRING); |
| | | ReplicationEnvironment environment = createReplicationEnv(rootPath); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1); |
| | | replicaDB2 = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_2, 1); |
| | | replicaDB = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_1, 1, cryptoSuite); |
| | | replicaDB2 = environment.getOrCreateReplicaDB(domainDN, SERVER_ID_2, 1, cryptoSuite); |
| | | |
| | | // delete the domain directory created for the 2 replica DBs to break the |
| | | // consistency with domain state file |