diff --git a/at_client/src/main/java/org/atsign/client/api/AtKeyNames.java b/at_client/src/main/java/org/atsign/client/api/AtKeyNames.java new file mode 100644 index 00000000..eee0d3ab --- /dev/null +++ b/at_client/src/main/java/org/atsign/client/api/AtKeyNames.java @@ -0,0 +1,57 @@ +package org.atsign.client.api; + +import org.atsign.common.AtSign; + +/** + * Constants and Utility methods for "well known" standard keys + */ +public class AtKeyNames { + + /** + * Key name for an Atsign's public encryption key + */ + public static final String PUBLIC_ENCRYPT = "publickey"; + + /** + * Key name used during activation of Atsign server with CRAM authentication + */ + public static final String PRIVATE_AT_SECRET = "privatekey:at_secret"; + + /** + * Key name used to store a shared encryption key between two {@link AtSign}s + */ + public static final String SHARED_KEY = "shared_key"; + + /** + * Key name used during enrollment completion, this is used to obtain the self encryption + * for an {@link AtSign} + */ + public static final String SELF_ENCRYPTION_KEY = "default_self_enc_key"; + + /** + * Key name used during enrollment completion, this is used to obtain the private encryption + * key for an {@link AtSign} + */ + public static final String ENCRYPT_PRIVATE_KEY = "default_enc_private_key"; + + /** + * Returns the key name used to store the shared encryption key encrypted with + * the sharedBy {@link AtSign}'s public key + * + * @param sharedWith the sharedWith (recipient) {@link AtSign} + * @return the fully qualified key name + */ + public static String toSharedByMeKeyName(AtSign sharedWith) { + return String.format("%s.%s", SHARED_KEY, sharedWith.withoutPrefix()); + } + + + /** + * + * @return the key used to shared + */ + public static String toSharedWithMeKeyName(AtSign sharedBy, AtSign sharedWith) { + return String.format("%s:%s%s", SHARED_KEY, sharedWith, sharedBy); + } + +} diff --git a/at_client/src/main/java/org/atsign/client/api/AtKeys.java b/at_client/src/main/java/org/atsign/client/api/AtKeys.java index 9af38c4d..3b5171d2 100644 --- a/at_client/src/main/java/org/atsign/client/api/AtKeys.java +++ b/at_client/src/main/java/org/atsign/client/api/AtKeys.java @@ -1,8 +1,7 @@ package org.atsign.client.api; +import java.security.Key; import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; import java.util.Base64; import java.util.Collections; import java.util.Map; @@ -10,50 +9,64 @@ import org.atsign.client.util.EnrollmentId; +import lombok.Builder; +import lombok.Value; + /** - * Data class used to hold an {@link org.atsign.client.api.AtClient}s keys + * An immutable class used to hold an {@link org.atsign.client.api.AtClient}s keys. + *

+ * Examples: + * + *

+ * AtKeys keys = AtKeys.builder()
+ *     .selfEncryptKey(generateAESKeyBase64())
+ *     .apkamKeyPair(generateRSAKeyPair())
+ *     .apkamSymmetricKey(generateAESKeyBase64())
+ *     .build();
+ *
+ * keys = keys.toBuilder().enrollmentId(enrollmentId).build();
+ * 
*/ +@Value +@Builder(toBuilder = true) public class AtKeys { /** * unique id which is assigned by the at_server at enrollment time, this is associated with a - * specific - * application and device and therefore a specific the apkam key pair + * specific application and device and therefore a specific the apkam key pair */ - private EnrollmentId enrollmentId; + EnrollmentId enrollmentId; /** * Public Key used for Authentication Management, once this is stored in the at_server then an * {@link org.atsign.client.api.AtClient} is able to authenticate using the corresponding private * key. */ - private String apkamPublicKey; + String apkamPublicKey; /** * Private Key used for Authentication Management, this is used to sign the challenge during * authentication with the at_server. */ - private String apkamPrivateKey; + String apkamPrivateKey; /** * Encryption Key used during enrollment. This key is sent as part of the enrollment request - * (encrypted - * with the atsigns public encryption key). The process that approves the enrollment request uses - * this - * key to encrypt the {@link #selfEncryptKey} and {@link #encryptPrivateKey} in the response that it - * sends. + * (encrypted with the atsigns public encryption key). The process that approves the enrollment + * request uses this key to encrypt the {@link #selfEncryptKey} and {@link #encryptPrivateKey} + * in the response that it sends. * The process which requested the enrollment can then decrypt and store those keys. * This ensures that all {@link AtKeys} got and {@link org.atsign.common.AtSign} share the same * {@link #selfEncryptKey} and {@link #encryptPrivateKey} */ - private String apkamSymmetricKey; + String apkamSymmetricKey; /** * Encryption Key used to encrypt {@link org.atsign.common.Keys.SelfKey}s and the pkam and * encryption key pairs * when they are externalised as JSON */ - private String selfEncryptKey; + String selfEncryptKey; /** * This is used to encrypt the symmetric keys that are used to encrypt @@ -61,136 +74,78 @@ public class AtKeys { * where the shared with {@link org.atsign.common.AtSign} is this {@link org.atsign.common.AtSign} */ - private String encryptPublicKey; + String encryptPublicKey; /** * This is used to decrypt the symmetric keys that are used to encrypt * {@link org.atsign.common.Keys.SharedKey}s * where the shared with {@link org.atsign.common.AtSign} is this {@link org.atsign.common.AtSign} */ - private String encryptPrivateKey; + String encryptPrivateKey; /** * Transient cache of other keys */ - private Map cache = new ConcurrentHashMap<>(); + Map cache = new ConcurrentHashMap<>(); + /** + * @return true if these {link @AtKeys} have enrollment id + */ public boolean hasEnrollmentId() { return enrollmentId != null; } - public EnrollmentId getEnrollmentId() { - return enrollmentId; - } - - public AtKeys setEnrollmentId(EnrollmentId enrollmentId) { - this.enrollmentId = enrollmentId; - return this; - } - - public String getSelfEncryptKey() { - return selfEncryptKey; - } - - public AtKeys setSelfEncryptKey(String key) { - this.selfEncryptKey = key; - return this; - } - - public String getApkamPublicKey() { - return apkamPublicKey; - } - - public AtKeys setApkamPublicKey(String key) { - this.apkamPublicKey = key; - return this; - } - - public AtKeys setApkamPublicKey(PublicKey key) { - return setApkamPublicKey(createStringBase64(key)); - } - - public String getApkamPrivateKey() { - return apkamPrivateKey; - } - - public AtKeys setApkamPrivateKey(String key) { - this.apkamPrivateKey = key; - return this; - } - - public AtKeys setApkamPrivateKey(PrivateKey key) { - return setApkamPrivateKey(createStringBase64(key)); - } - - public AtKeys setApkamKeyPair(KeyPair keyPair) { - setApkamPublicKey(keyPair.getPublic()); - setApkamPrivateKey(keyPair.getPrivate()); - return this; - } - - public boolean hasPkamKeys() { - return this.apkamPublicKey != null && this.apkamPrivateKey != null; - } - - public AtKeys setEncryptKeyPair(KeyPair keyPair) { - setEncryptPublicKey(keyPair.getPublic()); - setEncryptPrivateKey(keyPair.getPrivate()); - return this; - } - - public String getEncryptPublicKey() { - return encryptPublicKey; - } - - public AtKeys setEncryptPublicKey(String key) { - this.encryptPublicKey = key; - return this; - } - - public AtKeys setEncryptPublicKey(PublicKey key) { - return setEncryptPublicKey(createStringBase64(key)); - } - - public String getEncryptPrivateKey() { - return encryptPrivateKey; - } - - public AtKeys setEncryptPrivateKey(String key) { - this.encryptPrivateKey = key; - return this; - } - - public AtKeys setEncryptPrivateKey(PrivateKey key) { - return setEncryptPrivateKey(createStringBase64(key)); - } - - public String getApkamSymmetricKey() { - return apkamSymmetricKey; - } - - public AtKeys setApkamSymmetricKey(String key) { - this.apkamSymmetricKey = key; - return this; + /** + * @return true if these {link @AtKeys} have APKAM private key set + */ + public boolean hasPkamKey() { + return this.apkamPrivateKey != null; } + /** + * Allows the retrieval of previously put key values from the transient cache. + * + * @param key for the cached value + * @return cached value or null if no entry + */ public String get(String key) { return cache.get(key); } + /** + * Allows the storage key values in the transient cache. + * + * @param key for the cached value + * @param value the cached value + */ public void put(String key, String value) { cache.put(key, value); } + /** + * @return an immutable map of all the transient cached values + */ public Map getCache() { return Collections.unmodifiableMap(cache); } - private static String createStringBase64(PublicKey key) { - return Base64.getEncoder().encodeToString(key.getEncoded()); + /** + * Builder utility. + */ + public static class AtKeysBuilder { + + public AtKeysBuilder encryptKeyPair(KeyPair keyPair) { + return this.encryptPublicKey(createStringBase64(keyPair.getPublic())) + .encryptPrivateKey(createStringBase64(keyPair.getPrivate())); + } + + public AtKeysBuilder apkamKeyPair(KeyPair keyPair) { + return this.apkamPublicKey(createStringBase64(keyPair.getPublic())) + .apkamPrivateKey(createStringBase64(keyPair.getPrivate())); + } } - private static String createStringBase64(PrivateKey key) { + private static String createStringBase64(Key key) { return Base64.getEncoder().encodeToString(key.getEncoded()); } diff --git a/at_client/src/main/java/org/atsign/client/api/impl/clients/AtClientImpl.java b/at_client/src/main/java/org/atsign/client/api/impl/clients/AtClientImpl.java index d02c1441..0a97523f 100644 --- a/at_client/src/main/java/org/atsign/client/api/impl/clients/AtClientImpl.java +++ b/at_client/src/main/java/org/atsign/client/api/impl/clients/AtClientImpl.java @@ -1,27 +1,25 @@ package org.atsign.client.api.impl.clients; import static org.atsign.client.api.AtEvents.AtEventType.decryptedUpdateNotification; +import static org.atsign.client.api.AtKeyNames.toSharedByMeKeyName; import static org.atsign.client.util.Preconditions.checkNotNull; +import static org.atsign.common.VerbBuilders.*; +import static org.atsign.common.VerbBuilders.LookupOperation.all; +import static org.atsign.common.VerbBuilders.LookupOperation.meta; import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.TimeUnit; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; import lombok.extern.slf4j.Slf4j; import org.atsign.client.api.AtClient; import org.atsign.client.api.AtEvents.AtEventBus; import org.atsign.client.api.AtEvents.AtEventListener; import org.atsign.client.api.AtEvents.AtEventType; +import org.atsign.client.api.AtKeyNames; import org.atsign.client.api.AtKeys; import org.atsign.client.api.Secondary; import org.atsign.client.util.EncryptionUtil; @@ -30,13 +28,11 @@ import org.atsign.common.Keys.PublicKey; import org.atsign.common.Keys.SelfKey; import org.atsign.common.Keys.SharedKey; -import org.atsign.common.VerbBuilders.*; import org.atsign.common.exceptions.*; import org.atsign.common.options.GetRequestOptions; import org.atsign.common.response_models.LookupResponse; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; /** * Implementation of an {@link AtClient} which wraps a {@link Secondary} @@ -45,7 +41,6 @@ @SuppressWarnings({"RedundantThrows", "unused"}) @Slf4j public class AtClientImpl implements AtClient { - static final ObjectMapper json = new ObjectMapper(); // Factory method - creates an AtClientImpl with a RemoteSecondary @@ -142,7 +137,8 @@ public synchronized void handleEvent(AtEventType eventType, Map try { // decrypt it with the symmetric key that the other atSign shared with me - String encryptionKeySharedByOther = getEncryptionKeySharedByOther(SharedKey.fromString(key)); + SharedKey sk = Keys.sharedKeyBuilder().rawKey(key).build(); + String encryptionKeySharedByOther = getEncryptionKeySharedByOther(sk); String decryptedValue = EncryptionUtil.aesDecryptFromBase64(encryptedValue, encryptionKeySharedByOther, ivNonce); @@ -150,7 +146,7 @@ public synchronized void handleEvent(AtEventType eventType, Map newEventData.put("decryptedValue", decryptedValue); eventBus.publishEvent(decryptedUpdateNotification, newEventData); } catch (Exception e) { - log.error("caught exception {} while decrypting received data with key name [{}]", e, key); + log.error("caught exception while decrypting received data with key name [{}]", key, e); } } break; @@ -392,7 +388,7 @@ public void close() throws IOException { // Synchronous methods which do the actual work // private String _get(SharedKey sharedKey) throws AtException { - if (sharedKey.sharedBy.equals(atSign)) { + if (sharedKey.sharedBy().equals(atSign)) { return _getSharedByMeWithOther(sharedKey); } else { return _getSharedByOtherWithMe(sharedKey); @@ -402,13 +398,11 @@ private String _get(SharedKey sharedKey) throws AtException { private String _getSharedByMeWithOther(SharedKey sharedKey) throws AtException { String shareEncryptionKey = getEncryptionKeySharedByMe(sharedKey); - // fetch local - e.g. if I'm @bob, I would first "llookup:@alice:some.key.name@bob" - LlookupVerbBuilder commandBuilder = new LlookupVerbBuilder(); - commandBuilder.with(sharedKey, LlookupVerbBuilder.Type.ALL); - LookupResponse response = getLookupResponse(commandBuilder.build()); + String command = llookupCommandBuilder().key(sharedKey).operation(all).build(); + LookupResponse response = getLookupResponse(command); try { - return EncryptionUtil.aesDecryptFromBase64(response.data, shareEncryptionKey, response.metaData.ivNonce); + return EncryptionUtil.aesDecryptFromBase64(response.data, shareEncryptionKey, response.metaData.ivNonce()); } catch (Exception e) { throw new AtDecryptionException("Failed to decrypt value with shared encryption key", e); } @@ -418,22 +412,21 @@ private String _getSharedByOtherWithMe(SharedKey sharedKey) throws AtException { String what; String shareEncryptionKey = getEncryptionKeySharedByOther(sharedKey); - LookupVerbBuilder commandBuilder = new LookupVerbBuilder(); - commandBuilder.with(sharedKey, LookupVerbBuilder.Type.ALL); - LookupResponse response = getLookupResponse(commandBuilder.build()); + String command = lookupCommandBuilder().key(sharedKey).operation(all).build(); + LookupResponse response = getLookupResponse(command); what = "decrypt value with shared encryption key"; try { - return EncryptionUtil.aesDecryptFromBase64(response.data, shareEncryptionKey, response.metaData.ivNonce); + return EncryptionUtil.aesDecryptFromBase64(response.data, shareEncryptionKey, response.metaData.ivNonce()); } catch (Exception e) { throw new AtDecryptionException("Failed to " + what, e); } } private String _put(SharedKey sharedKey, String value) throws AtException { - if (!this.atSign.equals(sharedKey.sharedBy)) { + if (!this.atSign.equals(sharedKey.sharedBy())) { throw new AtIllegalArgumentException( - "sharedBy is [" + sharedKey.sharedBy + "] but should be this client's atSign [" + atSign + "]"); + "sharedBy is [" + sharedKey.sharedBy() + "] but should be this client's atSign [" + atSign + "]"); } String what = ""; String cipherText; @@ -443,13 +436,13 @@ private String _put(SharedKey sharedKey, String value) throws AtException { what = "encrypt value with shared encryption key"; String iv = EncryptionUtil.generateRandomIvBase64(16); - sharedKey.metadata.ivNonce = iv; + sharedKey.updateMissingMetadata(Metadata.builder().ivNonce(iv).build()); cipherText = EncryptionUtil.aesEncryptToBase64(value, shareToEncryptionKey, iv); } catch (Exception e) { throw new AtEncryptionException("Failed to " + what, e); } - String command = "update" + sharedKey.metadata.toString() + ":" + sharedKey + " " + cipherText; + String command = updateCommandBuilder().key(sharedKey).value(cipherText).build(); try { return secondary.executeCommand(command, true).toString(); @@ -459,7 +452,7 @@ private String _put(SharedKey sharedKey, String value) throws AtException { } private String _delete(SharedKey sharedKey) throws AtException { - String command = "delete:" + sharedKey; + String command = deleteCommandBuilder().key(sharedKey).build(); try { return secondary.executeCommand(command, true).toString(); } catch (IOException e) { @@ -469,10 +462,7 @@ private String _delete(SharedKey sharedKey) throws AtException { private String _get(SelfKey key) throws AtException { // 1. build command - String command; - LlookupVerbBuilder builder = new LlookupVerbBuilder(); - builder.with(key, LlookupVerbBuilder.Type.ALL); - command = builder.build(); + String command = llookupCommandBuilder().key(key).operation(all).build(); // 2. execute command LookupResponse fetched = getLookupResponse(command); @@ -481,37 +471,27 @@ private String _get(SelfKey key) throws AtException { String decryptedValue; String encryptedValue = fetched.data; String selfEncryptionKey = keys.getSelfEncryptKey(); - String iv = fetched.metaData.ivNonce; - try { - decryptedValue = EncryptionUtil.aesDecryptFromBase64(encryptedValue, selfEncryptionKey, iv); - } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException - | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException e) { - throw new AtDecryptionException("Failed to " + command, e); - } + String iv = checkNotNull(fetched.metaData.ivNonce(), "ivNonce is null"); + decryptedValue = EncryptionUtil.aesDecryptFromBase64(encryptedValue, selfEncryptionKey, iv); // 4. update metadata. squash the fetchedMetadata with current key.metadata (fetchedMetadata has higher priority) - key.metadata = Metadata.squash(fetched.metaData, key.metadata); + key.overwriteMetadata(fetched.metaData); return decryptedValue; } private String _put(SelfKey selfKey, String value) throws AtException { // 1. generate dataSignature - selfKey.metadata.dataSignature = generateSignature(value); - selfKey.metadata.ivNonce = EncryptionUtil.generateRandomIvBase64(16); + Metadata metadata = Metadata.builder() + .dataSignature(generateSignature(value)) + .ivNonce(EncryptionUtil.generateRandomIvBase64(16)) + .build(); + selfKey.updateMissingMetadata(metadata); // 2. encrypt data with self encryption key - String cipherText; - try { - cipherText = EncryptionUtil.aesEncryptToBase64(value, keys.getSelfEncryptKey(), selfKey.metadata.ivNonce); - } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException - | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException e) { - throw new AtEncryptionException("Failed to encrypt value with self encryption key", e); - } + String cipherText = EncryptionUtil.aesEncryptToBase64(value, keys.getSelfEncryptKey(), metadata.ivNonce()); // 3. update secondary - UpdateVerbBuilder builder = new UpdateVerbBuilder(); - builder.with(selfKey, cipherText); - String command = builder.build(); + String command = updateCommandBuilder().key(selfKey).value(cipherText).build(); try { return secondary.executeCommand(command, true).toString(); } catch (IOException e) { @@ -521,9 +501,7 @@ private String _put(SelfKey selfKey, String value) throws AtException { private String _delete(SelfKey key) throws AtException { // 1. build delete command - DeleteVerbBuilder builder = new DeleteVerbBuilder(); - builder.with(key); - String command = builder.build(); + String command = deleteCommandBuilder().key(key).build(); // 2. run command try { @@ -540,25 +518,19 @@ private String _get(PublicKey key) throws AtException { private String _get(PublicKey key, GetRequestOptions getRequestOptions) throws AtException { // 1. build command String command; - if (atSign.toString().equals(key.sharedBy.toString())) { - // it's a public key created by this client => llookup - LlookupVerbBuilder builder = new LlookupVerbBuilder(); - builder.with(key, LlookupVerbBuilder.Type.ALL); - command = builder.build(); + if (atSign.equals(key.sharedBy())) { + command = llookupCommandBuilder().key(key).operation(all).build(); } else { - // it's a public key created by another => plookup - PlookupVerbBuilder builder = new PlookupVerbBuilder(); - builder.with(key, PlookupVerbBuilder.Type.ALL); - builder.setBypassCache(getRequestOptions != null && getRequestOptions.getBypassCache()); - command = builder.build(); + boolean bypassCache = getRequestOptions != null && getRequestOptions.getBypassCache(); + command = plookupCommandBuilder().key(key).bypassCache(bypassCache).operation(all).build(); } // 2. run the command LookupResponse fetched = getLookupResponse(command); // 4. update key object metadata - key.metadata = Metadata.squash(fetched.metaData, key.metadata); - key.metadata.isCached = fetched.key.contains("cached:"); + Metadata metadata = fetched.metaData.toBuilder().isCached(fetched.key.contains("cached:")).build(); + key.overwriteMetadata(metadata); // 5. return the AtValue return fetched.data; @@ -566,13 +538,11 @@ private String _get(PublicKey key, GetRequestOptions getRequestOptions) throws A private String _put(PublicKey publicKey, String value) throws AtException { // 1. generate dataSignature - publicKey.metadata.dataSignature = generateSignature(value); + Metadata metadata = Metadata.builder().dataSignature(generateSignature(value)).build(); + publicKey.updateMissingMetadata(metadata); // 2. build command - String command; - UpdateVerbBuilder builder = new UpdateVerbBuilder(); - builder.with(publicKey, value); - command = builder.build(); + String command = updateCommandBuilder().key(publicKey).value(value).build(); // 3. run command try { @@ -584,10 +554,7 @@ private String _put(PublicKey publicKey, String value) throws AtException { private String _delete(PublicKey key) throws AtException { // 1. build command - String command; - DeleteVerbBuilder builder = new DeleteVerbBuilder(); - builder.with(key); - command = builder.build(); + String command = deleteCommandBuilder().key(key).build(); // 2. run command try { @@ -626,10 +593,7 @@ private String _put(PublicKey publicKey, byte[] value) throws AtException { } private List _getAtKeys(String regex, boolean fetchMetadata) throws AtException { - ScanVerbBuilder scanVerbBuilder = new ScanVerbBuilder(); - scanVerbBuilder.setRegex(regex); - scanVerbBuilder.setShowHidden(true); - String scanCommand = scanVerbBuilder.build(); + String scanCommand = scanCommandBuilder().regex(regex).showHidden(true).build(); Response scanRawResponse; try { scanRawResponse = executeCommand(scanCommand, true); @@ -641,10 +605,10 @@ private List _getAtKeys(String regex, boolean fetchMetadata) throws AtExc List rawArray = scanResponseTransformer.apply(scanRawResponse); List atKeys = new ArrayList<>(); - for (String atKeyRaw : rawArray) { // eg atKeyRaw == @bob:phone@alice - AtKey atKey = Keys.fromString(atKeyRaw); + for (String atKeyRaw : rawArray) { + AtKey atKey = Keys.keyBuilder().rawKey(atKeyRaw).build(); if (fetchMetadata) { - String llookupCommand = "llookup:meta:" + atKeyRaw; + String llookupCommand = llookupCommandBuilder().operation(meta).rawKey(atKeyRaw).build(); Response llookupMetaResponse; try { llookupMetaResponse = secondary.executeCommand(llookupCommand, true); @@ -652,7 +616,9 @@ private List _getAtKeys(String regex, boolean fetchMetadata) throws AtExc throw new AtSecondaryConnectException("Failed to execute " + llookupCommand, e); } try { - atKey.metadata = Metadata.squash(atKey.metadata, Metadata.fromJson(llookupMetaResponse.getRawDataResponse())); // atKey.metadata has priority over llookupMetaRaw.data + // atKey.metadata has priority over llookupMetaRaw.data + Metadata responseMetadata = Metadata.fromJson(llookupMetaResponse.getRawDataResponse()); + atKey.updateMissingMetadata(responseMetadata); } catch (JsonProcessingException e) { throw new AtResponseHandlingException("Failed to parse JSON " + llookupMetaResponse.getRawDataResponse(), e); } @@ -681,7 +647,7 @@ private LookupResponse getLookupResponse(String command) throws AtException { // 3. transform the data to a LlookupAllResponse object LookupResponse fetched; try { - fetched = json.readValue(response.getRawDataResponse(), LookupResponse.class); + fetched = Json.MAPPER.readValue(response.getRawDataResponse(), LookupResponse.class); } catch (JsonProcessingException e) { throw new AtResponseHandlingException("Failed to parse JSON " + response.getRawDataResponse(), e); } @@ -689,11 +655,8 @@ private LookupResponse getLookupResponse(String command) throws AtException { } private String getEncryptionKeySharedByMe(SharedKey key) throws AtException { - // llookup:shared_key.bob@alice Secondary.Response rawResponse; - String toLookup = "shared_key." + key.sharedWith.withoutPrefix() + atSign; - - String command = "llookup:" + toLookup; + String command = llookupCommandBuilder().keyName(toSharedByMeKeyName(key.sharedWith())).sharedBy(atSign).build(); try { rawResponse = secondary.executeCommand(command, false); } catch (IOException e) { @@ -709,17 +672,14 @@ private String getEncryptionKeySharedByMe(SharedKey key) throws AtException { } } - // When we stored it, we encrypted it with our encryption public key; so we need to decrypt it now with our encryption private key - try { - return EncryptionUtil.rsaDecryptFromBase64(rawResponse.getRawDataResponse(), keys.getEncryptPrivateKey()); - } catch (Exception e) { - throw new AtDecryptionException("Failed to decrypt " + toLookup, e); - } + // When we stored it, we encrypted it with our encryption public key; so we need to decrypt it now with our + // encryption private key + return EncryptionUtil.rsaDecryptFromBase64(rawResponse.getRawDataResponse(), keys.getEncryptPrivateKey()); } private String getEncryptionKeySharedByOther(SharedKey sharedKey) throws AtException { // Let's see if it's in our in-memory cache - String sharedSharedKeyName = sharedKey.getSharedSharedKeyName(); + String sharedSharedKeyName = AtKeyNames.toSharedWithMeKeyName(sharedKey.sharedBy(), sharedKey.sharedWith()); String sharedKeyValue = keys.get(sharedSharedKeyName); if (sharedKeyValue != null) { @@ -729,7 +689,7 @@ private String getEncryptionKeySharedByOther(SharedKey sharedKey) throws AtExcep String what = ""; // Not in memory so now let's try to fetch from remote - e.g. if I'm @bob, lookup:shared_key@alice - String lookupCommand = "lookup:" + "shared_key" + sharedKey.sharedBy; + String lookupCommand = lookupCommandBuilder().keyName(AtKeyNames.SHARED_KEY).sharedBy(sharedKey.sharedBy()).build(); Response rawResponse; try { rawResponse = secondary.executeCommand(lookupCommand, true); @@ -751,9 +711,9 @@ private String getEncryptionKeySharedByOther(SharedKey sharedKey) throws AtExcep private String createSharedEncryptionKey(SharedKey sharedKey) throws AtException { // We need their public key - String theirPublicEncryptionKey = getPublicEncryptionKey(sharedKey.sharedWith); + String theirPublicEncryptionKey = getPublicEncryptionKey(sharedKey.sharedWith()); if (theirPublicEncryptionKey == null) { - throw new AtKeyNotFoundException(" public key " + sharedKey.sharedWith + throw new AtKeyNotFoundException(" public key " + sharedKey.sharedWith() + " not found but service is running - maybe that AtSign has not yet been onboarded"); } @@ -762,7 +722,7 @@ private String createSharedEncryptionKey(SharedKey sharedKey) throws AtException try { aesKey = EncryptionUtil.generateAESKeyBase64(); } catch (Exception e) { - throw new AtEncryptionException("Failed to generate AES key for sharing with " + sharedKey.sharedWith, e); + throw new AtEncryptionException("Failed to generate AES key for sharing with " + sharedKey.sharedWith(), e); } String what = ""; @@ -776,13 +736,22 @@ private String createSharedEncryptionKey(SharedKey sharedKey) throws AtException String encryptedForUs = EncryptionUtil.rsaEncryptToBase64(aesKey, keys.getEncryptPublicKey()); what = "save encrypted shared key for us"; - secondary.executeCommand("update:" + "shared_key." + sharedKey.sharedWith.withoutPrefix() + sharedKey.sharedBy - + " " + encryptedForUs, true); + String updateForUs = updateCommandBuilder() + .keyName(toSharedByMeKeyName(sharedKey.sharedWith())) + .sharedBy(sharedKey.sharedBy()) + .value(encryptedForUs) + .build(); + secondary.executeCommand(updateForUs, true); what = "save encrypted shared key for them"; - long ttr = TimeUnit.HOURS.toMillis(24); - secondary.executeCommand("update:ttr:" + ttr + ":" + sharedKey.sharedWith + ":shared_key" + sharedKey.sharedBy - + " " + encryptedForOther, true); + String updateForOther = updateCommandBuilder() + .keyName(AtKeyNames.SHARED_KEY) + .sharedBy(sharedKey.sharedBy()) + .sharedWith(sharedKey.sharedWith()) + .ttr(TimeUnit.HOURS.toMillis(24)) + .value(encryptedForOther) + .build(); + secondary.executeCommand(updateForOther, true); } catch (Exception e) { throw new AtEncryptionException("Failed to " + what, e); } @@ -791,10 +760,9 @@ private String createSharedEncryptionKey(SharedKey sharedKey) throws AtException } private String getPublicEncryptionKey(AtSign sharedWith) throws AtException { - // plookup:publickey@alice Secondary.Response rawResponse; - String command = "plookup:publickey" + sharedWith; + String command = plookupCommandBuilder().keyName(AtKeyNames.PUBLIC_ENCRYPT).sharedBy(sharedWith).build(); try { rawResponse = secondary.executeCommand(command, false); } catch (IOException e) { diff --git a/at_client/src/main/java/org/atsign/client/api/impl/connections/AtMonitorConnection.java b/at_client/src/main/java/org/atsign/client/api/impl/connections/AtMonitorConnection.java index 43575ffb..dc80d9a5 100644 --- a/at_client/src/main/java/org/atsign/client/api/impl/connections/AtMonitorConnection.java +++ b/at_client/src/main/java/org/atsign/client/api/impl/connections/AtMonitorConnection.java @@ -15,6 +15,7 @@ import org.atsign.common.AtSign; import com.fasterxml.jackson.databind.ObjectMapper; +import org.atsign.common.Json; /** * A {@link AtMonitorConnection} represents a connection to an AtServer which, @@ -24,7 +25,8 @@ */ @Slf4j public class AtMonitorConnection extends AtSecondaryConnection implements Runnable { - private static final ObjectMapper mapper = new ObjectMapper(); + + private static final ObjectMapper mapper = Json.MAPPER; private long _lastReceivedTime = 0; diff --git a/at_client/src/main/java/org/atsign/client/cli/AbstractCli.java b/at_client/src/main/java/org/atsign/client/cli/AbstractCli.java index 4a1f6542..fa40866f 100644 --- a/at_client/src/main/java/org/atsign/client/cli/AbstractCli.java +++ b/at_client/src/main/java/org/atsign/client/cli/AbstractCli.java @@ -1,10 +1,10 @@ package org.atsign.client.cli; import static org.atsign.client.util.Preconditions.checkNotNull; +import static org.atsign.common.VerbBuilders.*; import java.io.File; import java.io.IOException; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -17,14 +17,12 @@ import org.atsign.client.api.impl.events.SimpleAtEventBus; import org.atsign.client.util.AuthUtil; import org.atsign.client.util.KeysUtil; -import org.atsign.client.util.TypedString; import org.atsign.common.AtException; import org.atsign.common.AtSign; +import org.atsign.common.Json; import org.atsign.common.exceptions.AtSecondaryNotFoundException; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import picocli.CommandLine.ITypeConverter; import picocli.CommandLine.Option; @@ -118,15 +116,17 @@ protected static File getAtKeysFile(File keysFile, AtSign atSign) { } protected static void checkAtServerMatchesAtSign(AtSecondaryConnection connection, AtSign atSign) throws IOException { - if (!matchDataJsonList(connection.executeCommand("scan")).contains("signing_publickey" + atSign)) { + String command = scanCommandBuilder().build(); + if (!matchDataJsonList(connection.executeCommand(command)).contains("signing_publickey" + atSign)) { // TODO: understand precisely what this means (observed in Dart SDK) throw new IllegalStateException("TBC"); } } - protected static void deleteKey(AtSecondaryConnection connection, String key) { + protected static void deleteKey(AtSecondaryConnection connection, String rawKey) { try { - match(connection.executeCommand("delete:" + key), DATA_INT); + String command = deleteCommandBuilder().rawKey(rawKey).build(); + match(connection.executeCommand(command), DATA_INT); } catch (IOException e) { throw new RuntimeException(e); } @@ -175,35 +175,9 @@ protected static String resolveSecondaryUrl(AtSign atSign, String rootUrl, int r throw ex; } - protected static String encodeKeyValuesAsJson(Object... nameValuePairs) throws Exception { - return encodeAsJson(toObjectMap(nameValuePairs)); - } - - protected static Map toObjectMap(Object... nameValuePairs) { - if ((nameValuePairs.length % 2) != 0) { - throw new IllegalArgumentException("odd number of parameters"); - } - Map map = new HashMap<>(); - for (int i = 0; i < nameValuePairs.length; i++) { - String key = nameValuePairs[i].toString(); - Object value = nameValuePairs[++i]; - if (value instanceof TypedString) { - map.put(key, value.toString()); - } else { - map.put(key, value); - } - } - return map; - } - - protected static String encodeAsJson(Map map) throws JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - return objectMapper.writeValueAsString(map); - } - protected static Map decodeJsonMapOfStrings(String json) { try { - return new ObjectMapper().readValue(json, new TypeReference>() {}); + return Json.MAPPER.readValue(json, new TypeReference>() {}); } catch (Exception e) { throw new RuntimeException(e); } @@ -211,7 +185,7 @@ protected static Map decodeJsonMapOfStrings(String json) { protected static Map decodeJsonMapOfObjects(String json) { try { - return new ObjectMapper().readValue(json, new TypeReference>() {}); + return Json.MAPPER.readValue(json, new TypeReference>() {}); } catch (Exception e) { throw new RuntimeException(e); } @@ -219,7 +193,7 @@ protected static Map decodeJsonMapOfObjects(String json) { protected static List decodeJsonList(String json) { try { - return new ObjectMapper().readValue(json, new TypeReference>() {}); + return Json.MAPPER.readValue(json, new TypeReference>() {}); } catch (Exception e) { throw new RuntimeException(e); } @@ -227,7 +201,7 @@ protected static List decodeJsonList(String json) { public static List decodeJsonListOfStrings(String json) { try { - return new ObjectMapper().readValue(json, new TypeReference>() {}); + return Json.MAPPER.readValue(json, new TypeReference>() {}); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/at_client/src/main/java/org/atsign/client/cli/Activate.java b/at_client/src/main/java/org/atsign/client/cli/Activate.java index 8d783389..9284a1e1 100644 --- a/at_client/src/main/java/org/atsign/client/cli/Activate.java +++ b/at_client/src/main/java/org/atsign/client/cli/Activate.java @@ -1,17 +1,10 @@ package org.atsign.client.cli; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; -import static org.atsign.client.util.EncryptionUtil.aesDecryptFromBase64; -import static org.atsign.client.util.EncryptionUtil.aesEncryptToBase64; -import static org.atsign.client.util.EncryptionUtil.generateAESKeyBase64; -import static org.atsign.client.util.EncryptionUtil.generateRSAKeyPair; -import static org.atsign.client.util.EncryptionUtil.generateRandomIvBase64; -import static org.atsign.client.util.EncryptionUtil.rsaDecryptFromBase64; -import static org.atsign.client.util.EncryptionUtil.rsaEncryptToBase64; +import static org.atsign.client.util.EncryptionUtil.*; import static org.atsign.client.util.EnrollmentId.createEnrollmentId; import static org.atsign.client.util.KeysUtil.saveKeys; import static org.atsign.client.util.Preconditions.checkNotNull; +import static org.atsign.common.VerbBuilders.*; import java.io.File; import java.io.IOException; @@ -23,6 +16,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import org.atsign.client.api.AtKeyNames; import org.atsign.client.api.AtKeys; import org.atsign.client.api.impl.connections.AtSecondaryConnection; import org.atsign.client.util.AuthUtil; @@ -30,6 +24,7 @@ import org.atsign.client.util.KeysUtil; import org.atsign.common.AtException; import org.atsign.common.AtSign; +import org.atsign.common.VerbBuilders; import org.atsign.common.exceptions.AtUnauthenticatedException; import picocli.CommandLine; @@ -208,7 +203,7 @@ public EnrollmentId onboard(AtSecondaryConnection connection) throws Exception { keys, ensureNotNull(appName, DEFAULT_FIRST_APP), ensureNotNull(deviceName, DEFAULT_FIRST_DEVICE)); - keys.setEnrollmentId(enrollmentId); + keys = keys.toBuilder().enrollmentId(enrollmentId).build(); authenticateWithApkam(connection, atSign, keys); saveKeys(keys, file); storeEncryptPublicKey(connection, atSign, keys); @@ -236,7 +231,10 @@ public List list(String status) throws Exception { } public List list(AtSecondaryConnection connection, String status) throws Exception { - String command = "enroll:list:" + encodeAsJson(singletonMap("enrollmentStatusFilter", singletonList(status))); + String command = enrollCommandBuilder() + .operation(VerbBuilders.EnrollOperation.list) + .status(status) + .build(); return matchDataJsonMapOfObjects(connection.executeCommand(command), true).keySet().stream() .map(Activate::inferEnrollmentId) .collect(Collectors.toList()); @@ -265,27 +263,35 @@ public void approve(AtSecondaryConnection connection, EnrollmentId enrollmentId) String key = fetchApkamSymmetricKey(connection, enrollmentId); String privateKeyIv = generateRandomIvBase64(16); String encryptPrivateKey = aesEncryptToBase64(keys.getEncryptPrivateKey(), key, privateKeyIv); - String selfKeyIv = generateRandomIvBase64(16); - String selfEncryptKey = aesEncryptToBase64(keys.getSelfEncryptKey(), key, selfKeyIv); - String json = encodeKeyValuesAsJson("enrollmentId", enrollmentId, - "encryptedDefaultEncryptionPrivateKey", encryptPrivateKey, - "encPrivateKeyIV", privateKeyIv, - "encryptedDefaultSelfEncryptionKey", selfEncryptKey, - "selfEncKeyIV", selfKeyIv); - - Map response = matchDataJsonMapOfStrings(connection.executeCommand("enroll:approve:" + json)); + String selfEncryptKeyIv = generateRandomIvBase64(16); + String selfEncryptKey = aesEncryptToBase64(keys.getSelfEncryptKey(), key, selfEncryptKeyIv); + + String command = enrollCommandBuilder() + .operation(VerbBuilders.EnrollOperation.approve) + .enrollmentId(enrollmentId) + .encryptPrivateKey(encryptPrivateKey) + .encryptPrivateKeyIv(privateKeyIv) + .selfEncryptKey(selfEncryptKey) + .selfEncryptKeyIv(selfEncryptKeyIv) + .build(); + + Map response = matchDataJsonMapOfStrings(connection.executeCommand(command)); if (!"approved".equals(response.get("status"))) { throw new RuntimeException("status is not approved : " + response.get("status")); } } private String fetchApkamSymmetricKey(AtSecondaryConnection connection, EnrollmentId enrollmentId) throws Exception { - String command = "enroll:fetch:" + encodeKeyValuesAsJson("enrollmentId", enrollmentId); + String command = enrollCommandBuilder() + .operation(VerbBuilders.EnrollOperation.fetch) + .enrollmentId(enrollmentId) + .build(); Map request = matchDataJsonMapOfObjects(connection.executeCommand(command)); if (!"pending".equals(request.get("status"))) { throw new RuntimeException("status is not pending : " + request.get("status")); } - String encryptedApkamSymmetricKey = (String) request.get("encryptedAPKAMSymmetricKey"); + String encryptedApkamSymmetricKey = + (String) request.get(VerbBuilders.EnrollParameters.ENCRYPTED_APKAM_SYMMETRIC_KEY); return rsaDecryptFromBase64(encryptedApkamSymmetricKey, keys.getEncryptPrivateKey()); } @@ -351,7 +357,8 @@ public String otp(AtSecondaryConnection connection) throws IOException, AtExcept File file = checkExists(getAtKeysFile(keysFile, atSign)); AtKeys keys = KeysUtil.loadKeys(file); authenticateWithApkam(connection, atSign, keys); - return match(connection.executeCommand("otp:get"), DATA_NON_WHITESPACE); + String command = otpCommandBuilder().build(); + return match(connection.executeCommand(command), DATA_NON_WHITESPACE); } public List scan() throws Exception { @@ -361,7 +368,11 @@ public List scan() throws Exception { } public List scan(AtSecondaryConnection connection) throws Exception { - return matchDataJsonListOfStrings(connection.executeCommand("scan:showHidden:true .*")); + String command = scanCommandBuilder() + .showHidden(true) + .regex(".*") + .build(); + return matchDataJsonListOfStrings(connection.executeCommand(command)); } private void singleArgEnrollAction(AtSecondaryConnection connection, @@ -369,7 +380,10 @@ private void singleArgEnrollAction(AtSecondaryConnection connection, EnrollmentId enrollmentId, String expectedStatus) throws Exception { - String command = "enroll:" + action + ":" + encodeKeyValuesAsJson("enrollmentId", enrollmentId); + String command = enrollCommandBuilder() + .operation(VerbBuilders.EnrollOperation.valueOf(action)) + .enrollmentId(enrollmentId) + .build(); Map map = matchDataJsonMapOfStrings(connection.executeCommand(command)); if (!expectedStatus.equals(map.get("status"))) { throw new RuntimeException("status is not " + expectedStatus + " : " + map.get("status")); @@ -387,11 +401,13 @@ private static EnrollmentId enroll(AtSecondaryConnection connection, String appName, String deviceName) throws Exception { - String json = encodeKeyValuesAsJson( - "appName", appName, - "deviceName", deviceName, - "apkamPublicKey", keys.getApkamPublicKey()); - Map response = matchDataJsonMapOfStrings(connection.executeCommand("enroll:request:" + json)); + String command = enrollCommandBuilder() + .operation(VerbBuilders.EnrollOperation.request) + .appName(appName) + .deviceName(deviceName) + .apkamPublicKey(keys.getApkamPublicKey()) + .build(); + Map response = matchDataJsonMapOfStrings(connection.executeCommand(command)); if (!response.get("status").equals("approved")) { throw new RuntimeException("enroll request failed, expected status approved : " + response); } @@ -400,22 +416,28 @@ private static EnrollmentId enroll(AtSecondaryConnection connection, protected static void storeEncryptPublicKey(AtSecondaryConnection connection, AtSign atSign, AtKeys keys) throws IOException { - match(connection.executeCommand("update:public:publickey" + atSign + " " + keys.getEncryptPublicKey()), DATA_INT); + String command = updateCommandBuilder() + .sharedBy(atSign) + .keyName(AtKeyNames.PUBLIC_ENCRYPT) + .isPublic(true) + .value(keys.getEncryptPublicKey()) + .build(); + match(connection.executeCommand(command), DATA_INT); } protected static void deleteCramSecret(AtSecondaryConnection connection) { - deleteKey(connection, "privatekey:at_secret"); + deleteKey(connection, AtKeyNames.PRIVATE_AT_SECRET); } protected static AtKeys generateAtKeys(boolean generateEncryptionKeyPair) throws NoSuchAlgorithmException { - AtKeys keys = new AtKeys() - .setSelfEncryptKey(generateAESKeyBase64()) - .setApkamKeyPair(generateRSAKeyPair()) - .setApkamSymmetricKey(generateAESKeyBase64()); + AtKeys.AtKeysBuilder builder = AtKeys.builder() + .selfEncryptKey(generateAESKeyBase64()) + .apkamKeyPair(generateRSAKeyPair()) + .apkamSymmetricKey(generateAESKeyBase64()); if (generateEncryptionKeyPair) { - keys.setEncryptKeyPair(generateRSAKeyPair()); + builder.encryptKeyPair(generateRSAKeyPair()); } - return keys; + return builder.build(); } public EnrollmentId enroll() throws Exception { @@ -425,28 +447,34 @@ public EnrollmentId enroll() throws Exception { } public EnrollmentId enroll(AtSecondaryConnection connection) throws Exception { - String publicKey = matchDataString(connection.executeCommand("lookup:publickey" + atSign)); + String command = lookupCommandBuilder() + .keyName(AtKeyNames.PUBLIC_ENCRYPT) + .sharedBy(atSign) + .build(); + String publicKey = matchDataString(connection.executeCommand(command)); File file = keysFile; if (!overwriteKeysFile) { checkNotExists(file); } - AtKeys keys = generateAtKeys(false); - keys.setEncryptPublicKey(publicKey); - keys.setEnrollmentId(enroll(connection, keys)); + AtKeys keys = generateAtKeys(false).toBuilder() + .encryptPublicKey(publicKey) + .build(); + enrollmentId = enroll(connection, keys); + keys = keys.toBuilder().enrollmentId(enrollmentId).build(); KeysUtil.saveKeys(keys, keysFile); return keys.getEnrollmentId(); } private EnrollmentId enroll(AtSecondaryConnection connection, AtKeys keys) throws Exception { - Map args = toObjectMap("appName", appName, - "deviceName", deviceName, - "apkamPublicKey", keys.getApkamPublicKey(), - "encryptedAPKAMSymmetricKey", - rsaEncryptToBase64(keys.getApkamSymmetricKey(), keys.getEncryptPublicKey()), - "otp", otp, - "namespaces", namespaces, - "apkamKeysExpiryInMillis", 0); - String command = "enroll:request:" + encodeAsJson(args); + String command = enrollCommandBuilder() + .operation(VerbBuilders.EnrollOperation.request) + .appName(appName) + .deviceName(deviceName) + .apkamPublicKey(keys.getApkamPublicKey()) + .apkamSymmetricKey(rsaEncryptToBase64(keys.getApkamSymmetricKey(), keys.getEncryptPublicKey())) + .otp(otp) + .namespaces(namespaces) + .build(); Map response = matchDataJsonMapOfStrings(connection.executeCommand(command)); if ("pending".equals(response.get("status"))) { return EnrollmentId.createEnrollmentId(response.get("enrollmentId")); @@ -464,8 +492,12 @@ public void complete() throws Exception { public void complete(AtSecondaryConnection connection) throws Exception { AtKeys keys = KeysUtil.loadKeys(keysFile); authenticate(connection); - keys.setSelfEncryptKey(keysGetDecrypted(connection, atSign, keys, "default_self_enc_key")); - keys.setEncryptPrivateKey(keysGetDecrypted(connection, atSign, keys, "default_enc_private_key")); + String selfEncryptKey = keysGetDecrypted(connection, atSign, keys, AtKeyNames.SELF_ENCRYPTION_KEY); + String encryptPrivateKey = keysGetDecrypted(connection, atSign, keys, AtKeyNames.ENCRYPT_PRIVATE_KEY); + keys = keys.toBuilder() + .selfEncryptKey(selfEncryptKey) + .encryptPrivateKey(encryptPrivateKey) + .build(); KeysUtil.saveKeys(keys, keysFile); } @@ -499,8 +531,11 @@ private static String keysGetDecrypted(AtSecondaryConnection connection, AtKeys keys, String keyConstant) throws Exception { - EnrollmentId enrollmentId = keys.getEnrollmentId(); - String command = "keys:get:keyName:" + enrollmentId + "." + keyConstant + ".__manage" + atSign; + String rawKeyName = keys.getEnrollmentId() + "." + keyConstant + ".__manage" + atSign; + String command = keysCommandBuilder() + .operation(VerbBuilders.KeysOperation.get) + .keyName(rawKeyName) + .build(); return decryptEncryptedKey(connection.executeCommand(command), keys.getApkamSymmetricKey()); } diff --git a/at_client/src/main/java/org/atsign/client/cli/Delete.java b/at_client/src/main/java/org/atsign/client/cli/Delete.java index 1dcaf01e..c52372ab 100644 --- a/at_client/src/main/java/org/atsign/client/cli/Delete.java +++ b/at_client/src/main/java/org/atsign/client/cli/Delete.java @@ -1,13 +1,13 @@ package org.atsign.client.cli; -import lombok.extern.slf4j.Slf4j; import org.atsign.client.api.AtClient; import org.atsign.client.api.AtKeys; import org.atsign.client.util.KeysUtil; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; import org.atsign.common.Keys; +import lombok.extern.slf4j.Slf4j; + /** * A command-line interface half-example half-utility to delete something that was previously shared */ @@ -32,8 +32,10 @@ public static void main(String[] args) throws Exception { try (AtClient atClient = AtClient.withRemoteSecondary(rootUrl, atSign, keys, true)) { - Keys.SharedKey key = new KeyBuilders.SharedKeyBuilder(atSign, otherAtSign) - .key(keyName) + Keys.SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(atSign) + .sharedWith(otherAtSign) + .name(keyName) .build(); String response = atClient.delete(key).get(); diff --git a/at_client/src/main/java/org/atsign/client/cli/Get.java b/at_client/src/main/java/org/atsign/client/cli/Get.java index 90d17994..afc23334 100644 --- a/at_client/src/main/java/org/atsign/client/cli/Get.java +++ b/at_client/src/main/java/org/atsign/client/cli/Get.java @@ -1,13 +1,13 @@ package org.atsign.client.cli; -import lombok.extern.slf4j.Slf4j; import org.atsign.client.api.AtClient; import org.atsign.client.api.AtKeys; import org.atsign.client.util.KeysUtil; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; import org.atsign.common.Keys; +import lombok.extern.slf4j.Slf4j; + /** * A command-line interface half-example half-utility to get something that was shared by another * atSign @@ -32,8 +32,10 @@ public static void main(String[] args) throws Exception { try (AtClient atClient = AtClient.withRemoteSecondary(rootUrl, atSign, keys, true)) { - Keys.SharedKey key = new KeyBuilders.SharedKeyBuilder(otherAtSign, atSign) - .key(keyName) + Keys.SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(otherAtSign) + .sharedWith(atSign) + .name(keyName) .build(); String response = atClient.get(key).get(); diff --git a/at_client/src/main/java/org/atsign/client/cli/Scan.java b/at_client/src/main/java/org/atsign/client/cli/Scan.java index 7ea83033..74ec7013 100644 --- a/at_client/src/main/java/org/atsign/client/cli/Scan.java +++ b/at_client/src/main/java/org/atsign/client/cli/Scan.java @@ -64,7 +64,7 @@ private static void printKeys(List keys, PrintStream out) { out.println("atKeys: {"); for (int i = 0; i < keys.size(); i++) { AtKey key = keys.get(i); - out.println(" " + i + ": " + (key.metadata.isCached ? "cached:" : "") + key); + out.println(" " + i + ": " + (key.metadata().isCached() ? "cached:" : "") + key); } out.println("}"); } @@ -72,36 +72,36 @@ private static void printKeys(List keys, PrintStream out) { private static void printKeyInfo(AtKey key, PrintStream out) { out.println("======================"); out.println("Full KeyName: " + key.toString()); - out.println("KeyName: " + key.name); - out.println("Namespace: " + key.getNamespace()); - out.println("SharedBy: " + key.sharedBy.atSign); - out.println("SharedWith: " + (key.sharedWith != null ? key.sharedWith.atSign : "null")); + out.println("KeyName: " + key.nameWithoutNamespace()); + out.println("Namespace: " + key.namespace()); + out.println("SharedBy: " + key.sharedBy()); + out.println("SharedWith: " + (key.sharedWith() != null ? key.sharedWith() : "null")); out.println("KeyType: " + key.getClass().toString().split("\\$")[1]); out.println("Metadata -------------------"); - printKeyMetadata(key.metadata, out); + printKeyMetadata(key.metadata(), out); out.println("======================"); out.println(); } private static void printKeyMetadata(Metadata metadata, PrintStream out) { - out.println("ttl: " + metadata.ttl); - out.println("ttb: " + metadata.ttb); - out.println("ttr: " + metadata.ttr); - out.println("ccd: " + metadata.ccd); - out.println("availableAt: " + (metadata.availableAt != null ? metadata.availableAt.toString() : "null")); - out.println("expiresAt: " + (metadata.expiresAt != null ? metadata.expiresAt.toString() : "null")); - out.println("refreshAt: " + (metadata.refreshAt != null ? metadata.refreshAt.toString() : "null")); - out.println("createdAt: " + (metadata.createdAt != null ? metadata.createdAt.toString() : "null")); - out.println("updatedAt: " + (metadata.updatedAt != null ? metadata.updatedAt.toString() : "null")); - out.println("dataSignature: " + metadata.dataSignature); - out.println("sharedKeyStatus: " + metadata.sharedKeyStatus); - out.println("isPublic: " + metadata.isPublic); - out.println("isEncrypted: " + metadata.isEncrypted); - out.println("isHidden: " + metadata.isHidden); - out.println("namespaceAware: " + metadata.namespaceAware); - out.println("isBinary: " + metadata.isBinary); - out.println("isCached: " + metadata.isCached); - out.println("sharedKeyEnc: " + metadata.sharedKeyEnc); - out.println("pubKeyCS: " + metadata.pubKeyCS); + out.println("ttl: " + metadata.ttl()); + out.println("ttb: " + metadata.ttb()); + out.println("ttr: " + metadata.ttr()); + out.println("ccd: " + metadata.ccd()); + out.println("availableAt: " + (metadata.availableAt() != null ? metadata.availableAt().toString() : "null")); + out.println("expiresAt: " + (metadata.expiresAt() != null ? metadata.expiresAt().toString() : "null")); + out.println("refreshAt: " + (metadata.refreshAt() != null ? metadata.refreshAt().toString() : "null")); + out.println("createdAt: " + (metadata.createdAt() != null ? metadata.createdAt().toString() : "null")); + out.println("updatedAt: " + (metadata.updatedAt() != null ? metadata.updatedAt().toString() : "null")); + out.println("dataSignature: " + metadata.dataSignature()); + out.println("sharedKeyStatus: " + metadata.sharedKeyStatus()); + out.println("isPublic: " + metadata.isPublic()); + out.println("isEncrypted: " + metadata.isEncrypted()); + out.println("isHidden: " + metadata.isHidden()); + out.println("namespaceAware: " + metadata.namespaceAware()); + out.println("isBinary: " + metadata.isBinary()); + out.println("isCached: " + metadata.isCached()); + out.println("sharedKeyEnc: " + metadata.sharedKeyEnc()); + out.println("pubKeyCS: " + metadata.pubKeyCS()); } } diff --git a/at_client/src/main/java/org/atsign/client/cli/Share.java b/at_client/src/main/java/org/atsign/client/cli/Share.java index a95aed67..f0866d23 100644 --- a/at_client/src/main/java/org/atsign/client/cli/Share.java +++ b/at_client/src/main/java/org/atsign/client/cli/Share.java @@ -4,7 +4,6 @@ import org.atsign.client.api.AtKeys; import org.atsign.client.util.KeysUtil; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; import org.atsign.common.Keys; import lombok.extern.slf4j.Slf4j; @@ -33,10 +32,12 @@ public static void main(String[] args) throws Exception { AtKeys keys = KeysUtil.loadKeys(atSign); try (AtClient atClient = AtClient.withRemoteSecondary(rootUrl, atSign, keys, true)) { - - Keys.SharedKey key = new KeyBuilders.SharedKeyBuilder(atSign, otherAtSign) - .key(keyName) - .cache(ttr, true) + Keys.SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(atSign) + .sharedWith(otherAtSign) + .name(keyName) + .ccd(true) + .ttr((long) ttr) .build(); String response = atClient.put(key, toShare).get(); diff --git a/at_client/src/main/java/org/atsign/client/util/AtClientValidation.java b/at_client/src/main/java/org/atsign/client/util/AtClientValidation.java index c651bc2b..88f98ce7 100644 --- a/at_client/src/main/java/org/atsign/client/util/AtClientValidation.java +++ b/at_client/src/main/java/org/atsign/client/util/AtClientValidation.java @@ -5,65 +5,13 @@ import org.atsign.client.api.Secondary; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.Keys.AtKey; -import org.atsign.common.Metadata; import org.atsign.common.exceptions.AtIllegalArgumentException; -import org.atsign.common.exceptions.AtInvalidAtKeyException; import org.atsign.common.exceptions.AtSecondaryConnectException; /** * Utility class with key string validation methods */ public class AtClientValidation { - // TODO Lots of atServer-address-finding going on here. Need a caching finder. - // TODO Change all of these static methods. - /** - * @param keyName // e.g. "test" (not the fullKeyName like "public:test@bob") - */ - public static void validateKeyName(String keyName) throws AtException { - // key cannot be null and cannot be the empty string (length 0) - if (keyName == null || keyName.isEmpty()) { - throw new AtInvalidAtKeyException("Key cannot be null or empty"); - } - - // key cannot have spaces - if (keyName.contains(" ")) { - throw new AtInvalidAtKeyException("Key cannot have spaces"); - } - - // Key cannot contain @ - if (keyName.contains("@")) { - throw new AtInvalidAtKeyException("Key cannot contain @"); - } - } - - /** - * - * @param metadata Metadata object to validate - * @throws AtException if metadata is null or has invalid values - */ - public static void validateMetadata(Metadata metadata) throws AtException { - // null check - if (metadata == null) { - throw new AtInvalidAtKeyException("Metadata cannot be null"); - } - - // validate ttl - if (metadata.ttl == null || (metadata.ttl < 0)) { - throw new AtInvalidAtKeyException("ttl cannot be null and cannot be negative"); - } - - // validate ttb - if (metadata.ttb == null || metadata.ttb < 0) { - throw new AtInvalidAtKeyException("ttb cannot be null and cannot be negative"); - } - - // validate ttr - if (metadata.ttr == null || metadata.ttr < -1) { - throw new AtInvalidAtKeyException("ttr cannot be null and cannot be < -1"); - } - - } /** * Checks if an atSign exists on a root server @@ -88,35 +36,4 @@ public static void atSignExists(AtSign atSign, String rootUrl) throws AtExceptio } } - /** - * Validate if an AtKey object is valid 1. checks if atKey.name is a valid keyName 2. checks if - * atKey.metadata has valid values 3. checks if atKey.sharedWith atSign is valid (if it exists) - * - * @param atKey AtKey object created usually through KeyBuilders - * @param rootUrl RootURL of the Root Server (e.g. "root.atsign.org:64") - * @throws AtException if the AtKey is invalid - */ - public static void validateAtKey(AtKey atKey, String rootUrl) throws AtException { - // 1. null check - if (atKey == null) { - throw new AtIllegalArgumentException("AtKey cannot be null"); - } - - if (rootUrl == null || rootUrl.isEmpty()) { - throw new AtIllegalArgumentException("RootURL cannot be null or empty"); - } - - // 2. validate key name - validateKeyName(atKey.name); - - // 3. validate metadata - validateMetadata(atKey.metadata); - - // 4. validate sharedWith exists - if (atKey.sharedWith != null) { - atSignExists(atKey.sharedWith, rootUrl); - } - - } - } diff --git a/at_client/src/main/java/org/atsign/client/util/AuthUtil.java b/at_client/src/main/java/org/atsign/client/util/AuthUtil.java index 58e278e5..7f1de8d0 100644 --- a/at_client/src/main/java/org/atsign/client/util/AuthUtil.java +++ b/at_client/src/main/java/org/atsign/client/util/AuthUtil.java @@ -1,5 +1,7 @@ package org.atsign.client.util; +import static org.atsign.common.VerbBuilders.*; + import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -31,7 +33,7 @@ public class AuthUtil { */ public void authenticateWithCram(AtSecondaryConnection connection, AtSign atSign, String cramSecret) throws AtException, IOException { - String fromResponse = connection.executeCommand("from:" + atSign); + String fromResponse = connection.executeCommand(fromCommandBuilder().atSign(atSign).build()); if (!fromResponse.startsWith("data:")) { throw new AtUnauthenticatedException("Invalid response to 'from': " + fromResponse); } @@ -44,7 +46,7 @@ public void authenticateWithCram(AtSecondaryConnection connection, AtSign atSign throw new AtEncryptionException("Failed to generate cramDigest", e); } - String cramResponse = connection.executeCommand("cram:" + cramDigest); + String cramResponse = connection.executeCommand(cramCommandBuilder().digest(cramDigest).build()); if (!cramResponse.startsWith("data:success")) { throw new AtUnauthenticatedException("CRAM command failed: " + cramResponse); } @@ -61,11 +63,11 @@ public void authenticateWithCram(AtSecondaryConnection connection, AtSign atSign */ public void authenticateWithPkam(AtConnection connection, AtSign atSign, AtKeys keys) throws AtException, IOException { - if (!keys.hasPkamKeys()) { + if (!keys.hasPkamKey()) { throw new AtClientConfigException("Cannot authenticate with PKAM: Keys file does not contain PKAM keys"); } - String fromResponse = connection.executeCommand("from:" + atSign); + String fromResponse = connection.executeCommand(fromCommandBuilder().atSign(atSign).build()); String dataPrefix = "data:"; if (!fromResponse.startsWith(dataPrefix)) { @@ -87,15 +89,15 @@ public void authenticateWithPkam(AtConnection connection, AtSign atSign, AtKeys throw new AtEncryptionException("Failed to create SHA256 signature"); } - StringBuilder builder = new StringBuilder().append("pkam"); + PkamCommandBuilder builder = pkamCommandBuilder(); if (keys.hasEnrollmentId()) { - builder.append(":signingAlgo:").append(EncryptionUtil.SIGNING_ALGO_RSA) - .append(":hashingAlgo:").append(EncryptionUtil.HASHING_ALGO_SHA256) - .append(":enrollmentId:").append(keys.getEnrollmentId()); + builder.signingAlgo(EncryptionUtil.SIGNING_ALGO_RSA); + builder.hashingAlgo(EncryptionUtil.HASHING_ALGO_SHA256); + builder.enrollmentId(keys.getEnrollmentId()); } - builder.append(":").append(signature); + builder.digest(signature); - String pkamResponse = connection.executeCommand(builder.toString()); + String pkamResponse = connection.executeCommand(builder.build()); if (!pkamResponse.startsWith("data:success")) { throw new AtUnauthenticatedException("PKAM command failed: " + pkamResponse); diff --git a/at_client/src/main/java/org/atsign/client/util/EncryptionUtil.java b/at_client/src/main/java/org/atsign/client/util/EncryptionUtil.java index fcaaa4fd..68a7e8f2 100644 --- a/at_client/src/main/java/org/atsign/client/util/EncryptionUtil.java +++ b/at_client/src/main/java/org/atsign/client/util/EncryptionUtil.java @@ -12,6 +12,8 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.atsign.common.exceptions.AtDecryptionException; +import org.atsign.common.exceptions.AtEncryptionException; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** @@ -28,19 +30,35 @@ public class EncryptionUtil { } public static String aesEncryptToBase64(String clearText, String keyBase64, String ivNonce) - throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, - IllegalBlockSizeException, BadPaddingException, NoSuchProviderException { - Cipher cipher = createAesCipher(Cipher.ENCRYPT_MODE, keyBase64, ivNonce); - byte[] encrypted = cipher.doFinal(clearText.getBytes()); - return Base64.getEncoder().encodeToString(encrypted); + throws AtEncryptionException { + try { + Cipher cipher = createAesCipher(Cipher.ENCRYPT_MODE, keyBase64, ivNonce); + byte[] encrypted = cipher.doFinal(clearText.getBytes()); + return Base64.getEncoder().encodeToString(encrypted); + } catch (NoSuchAlgorithmException | NoSuchProviderException | BadPaddingException | IllegalBlockSizeException + | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { + throw new AtEncryptionException("AES encryption failed", e); + } } - public static String aesDecryptFromBase64(String cipherTextBase64, String keyBase64, String ivNonce) - throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, - IllegalBlockSizeException, BadPaddingException, NoSuchProviderException { - Cipher cipher = createAesCipher(Cipher.DECRYPT_MODE, keyBase64, ivNonce); - byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(cipherTextBase64)); - return new String(decrypted); + /** + * Decrypts the text using AES {@link Cipher} + * + * @param text base64 encoded text to decrypt + * @param key base64 encoded AES key to use + * @param iv base64 encoded initialization vector to use + * @return decrypted text + * @throws AtDecryptionException with underlying cause + */ + public static String aesDecryptFromBase64(String text, String key, String iv) throws AtDecryptionException { + try { + Cipher cipher = createAesCipher(Cipher.DECRYPT_MODE, key, iv); + byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(text)); + return new String(decrypted); + } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + throw new AtDecryptionException("AES decryption failed", e); + } } public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException { @@ -57,27 +75,33 @@ public static String generateAESKeyBase64() throws NoSuchAlgorithmException { } public static String rsaDecryptFromBase64(String cipherTextBase64, String privateKeyBase64) - throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, - IllegalBlockSizeException, BadPaddingException { - PrivateKey privateKey = _privateKeyFromBase64(privateKeyBase64); - Cipher decryptCipher = Cipher.getInstance("RSA"); - decryptCipher.init(Cipher.DECRYPT_MODE, privateKey); - byte[] decoded = Base64.getDecoder().decode(cipherTextBase64.getBytes(StandardCharsets.UTF_8)); - byte[] decryptedMessageBytes = decryptCipher.doFinal(decoded); - - return new String(decryptedMessageBytes, StandardCharsets.UTF_8); + throws AtDecryptionException { + try { + PrivateKey privateKey = _privateKeyFromBase64(privateKeyBase64); + Cipher decryptCipher = Cipher.getInstance("RSA"); + decryptCipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] decoded = Base64.getDecoder().decode(cipherTextBase64.getBytes(StandardCharsets.UTF_8)); + byte[] decryptedMessageBytes = decryptCipher.doFinal(decoded); + return new String(decryptedMessageBytes, StandardCharsets.UTF_8); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException + | IllegalBlockSizeException | BadPaddingException e) { + throw new AtDecryptionException("RSA decryption failed", e); + } } public static String rsaEncryptToBase64(String clearText, String publicKeyBase64) - throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, - IllegalBlockSizeException, BadPaddingException { - PublicKey publicKey = _publicKeyFromBase64(publicKeyBase64); - Cipher encryptCipher = Cipher.getInstance("RSA"); - encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey); - byte[] clearTextBytes = clearText.getBytes(StandardCharsets.UTF_8); - byte[] encryptedMessageBytes = encryptCipher.doFinal(clearTextBytes); - - return Base64.getEncoder().encodeToString(encryptedMessageBytes); + throws AtEncryptionException { + try { + PublicKey publicKey = _publicKeyFromBase64(publicKeyBase64); + Cipher encryptCipher = Cipher.getInstance("RSA"); + encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] clearTextBytes = clearText.getBytes(StandardCharsets.UTF_8); + byte[] encryptedMessageBytes = encryptCipher.doFinal(clearTextBytes); + return Base64.getEncoder().encodeToString(encryptedMessageBytes); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException + | IllegalBlockSizeException | BadPaddingException e) { + throw new AtEncryptionException("RSA encryption failed", e); + } } public static String signSHA256RSA(String value, String privateKeyBase64) diff --git a/at_client/src/main/java/org/atsign/client/util/KeyStringUtil.java b/at_client/src/main/java/org/atsign/client/util/KeyStringUtil.java deleted file mode 100644 index 5c88eaeb..00000000 --- a/at_client/src/main/java/org/atsign/client/util/KeyStringUtil.java +++ /dev/null @@ -1,238 +0,0 @@ -package org.atsign.client.util; - -import static org.atsign.client.util.Preconditions.checkNotNull; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Utility class which parses a key string and can be used to extract key characteristics. - * Specifically: - *
    - *
  • {@link KeyType}
  • - *
  • namespace
  • - *
  • unqualfied key name
  • - *
  • "shared by" {@link org.atsign.common.AtSign}
  • - *
  • "shared with" {@link org.atsign.common.AtSign}
  • - *
  • whether key is cached on the {@link org.atsign.client.api.Secondary}
  • - *
  • whether key is hidden
  • - *
- */ -public class KeyStringUtil { - - private static final Pattern NAMESPACE_QUALIFIED_KEY_NAME = Pattern.compile("^(?!shared_key)(.+)\\.([^.]+)$"); - - /** - * Different types of keys in the Atsign Platform - */ - public enum KeyType { - PUBLIC_KEY, // PublicKey - SHARED_KEY, // SharedKey - SELF_KEY, // SelfKey - PRIVATE_HIDDEN_KEY, // PrivateHiddenKey - ; - } - - private String _fullKeyName; // e.g. "public:publickey@alice" - - private String _keyName; // should never be null (otherwise it's an error) - private KeyType _keyType; // see enum above, should never be null (otherwise it's an error) - private String _namespace; // nullable (some keys don't have namespaces) - - private String _sharedBy; // should never be null (all keys have a sharedBy atsign) - private String _sharedWith; // nullable - - private boolean _isCached; // true if key starts with "cached:" - private boolean _isHidden; // true if key contains "_" - - /** - * Constructor - * - * @param fullKeyName full key name e.g. "public:publickey@bob" - */ - public KeyStringUtil(String fullKeyName) { - this._fullKeyName = fullKeyName; - this._evaluate(fullKeyName); - } - - /** - * Returns the full key name (originally passed into the constructor) - * - * @return fullKeyName (what was originally passed into the constructor) - */ - public String getFullKeyName() { - return this._fullKeyName; - } - - /** - * Returns the key name (e.g. "publickey" from "public:publickey@alice") - * This value is evaluated from the private _evaluate method that is called in the constructor - * - * @return the key name - */ - public String getKeyName() { - return this._keyName; - } - - /** - * Returns the namespace of a key (no implementation yet) - * - * @return the namespace from a key (e.g. "mospherepro" from "file_1.mospherepro@alice") - */ - public String getNamespace() { // no namespace implementation in _evaluate - return this._namespace; - } - - /** - * Returns the key type enum of the key type evaluated from the private _evaluate method - * - * @return KeyStringUtil.KeyType (e.g. KeyStringUtil.KeyType.PUBLIC_KEY) - */ - public KeyType getKeyType() { - return this._keyType; - } - - /** - * Returns the sharedBy atSign that is evlauated from the _evaluate private method. - * - * @return the sharedBy atSign String (e.g. "@alice" from "test@alice") - */ - public String getSharedBy() { - return this._sharedBy; - } - - /** - * Returns the sharedWith atSign that is evlauated from the _evaluate private method. - * - * @return the sharedWith atSign String (e.g. "@bob" from "@bob:test@alice") - */ - public String getSharedWith() { - return this._sharedWith; - } - - /** - * Returns true if the key is cached (e.g. "cached:public:publickey@alice") - * - * @return true if the fullKeyName begins with "cached:" - */ - public boolean isCached() { - return this._isCached; - } - - /** - * Returns true if the key is hidden by default in scan - * - * @return true if the fullKeyName begins with "_" - */ - public boolean isHidden() { - return this._isHidden; - } - - /** - * Given the fullKeyName, this method will evaluate all of the properties that can be exactracted - * from the - * fullKeyName. Example: fullKeyName "test@bob" will evaluate sharedBy to be "@bob" and keyName to - * be "test" - * - * @param fullKeyName the fullKeyName to be evaluated (e.g. "test@bob") - */ - private void _evaluate(String fullKeyName) { - // Examples: - // (1) PublicKey == public:signing_publickey@smoothalligator - // (2) PublicKey (cached) == cached:public:publickey@denise - // (3) SharedKey == @abbcservicesinc:shared_key@smoothalligator - // (4) SharedKey (cached) == cached:@smoothalligator:shared_key@abbcservicesinc - // (5) PrivateHiddenKey == _latestnotificationid.fourballcorporate9@smoothalligator - // (6) SelfKey == shared_key.wildgreen@smoothalligator - // (7) SelfKey == @smoothalligator:lemon@smoothalligator - String[] split1 = fullKeyName.split(":"); - // split1 results - // 1 == {"public", "signing_publickey@smoothalligator"} [len 2] - // 2 == {"cached", "public", "publickey@denise"} [len 3] - // 3 == {"@abbcservicesinc", "shared_key@smoothalligator"} [len 2] - // 4 == {"cached", "@smoothalligator", "shared_key@abbcservicesinc"} [len 3] - // 5 == {"_latestnotificationid.fourballcorporate9@smoothalligator"} [len 1] - // 6 == {"shared_key.wildgreen@smoothalligator"} [len 1] - - - if (split1.length > 1) { - // must be scenarios 1, 2, 3, 4, - - // PublicKey check - if (split1[0].equals("public") || (split1[0].equals("cached") && split1[1].equals("public"))) { - // scenario 1 and 2,, it's a public key! - _keyType = KeyType.PUBLIC_KEY; - } else if (split1[0].equals("private") || split1[0].equals("privatekey")) { - _keyType = KeyType.PRIVATE_HIDDEN_KEY; - _isHidden = true; - } - - if (split1[0].startsWith("@") || split1[1].startsWith("@")) { - // scenario 3 and 4,, it is a SharedKey! - if (_keyType == null) { - _keyType = KeyType.SHARED_KEY; // don't want to overwrite the above checks - } - if (split1[0].startsWith("@")) { - _sharedWith = split1[0].substring(1); - } else { - _sharedWith = split1[1].substring(1); - } - } - - String[] split2 = split1[split1.length - 1].split("@"); - // 1 == {"signing_publickey", "smoothalligator"} - // 2 == {"publickey", "denise"} - // 3 == {"shared_key", "smoothalligator"} - // 4 == {"shared_key", "abbcservicesinc"} - _keyName = checkNotNull(split2[0], "key name is null"); - _sharedBy = checkNotNull(split2[1], "shared by is null"); - - // PublicKey and SharedKey can be cacheable! - if (split1[0].equals("cached")) { - _isCached = true; - } - - // _sharedBy == _sharedWith => it's a SelfKey - if (_sharedBy.equals(_sharedWith)) { - _keyType = KeyType.SELF_KEY; - } - - } else { - // must be scenarios 5 and 6 - if (split1[0].startsWith("_")) { - _keyType = KeyType.PRIVATE_HIDDEN_KEY; - } else { - _keyType = KeyType.SELF_KEY; - } - - String[] split2 = split1[0].split("@"); - // 5 == {"_latestnotificationid.fourballcorporate9", "smoothalligator"} - // 6 == {"shared_key.wildgreen", "smoothalligator"} - _keyName = checkNotNull(split2[0], "key name is null"); - _sharedBy = checkNotNull(split2[1], "shared by is null"); - - } - - Matcher matcher = createNamespaceQualifiedKeyNameMatcher(_keyName); - if (matcher.matches()) { - _keyName = matcher.group(1); - _namespace = matcher.group(2); - } else { - _namespace = null; - } - - if (_sharedBy != null) { - _sharedBy = "@" + _sharedBy; // add atSign in front - } - if (_sharedWith != null) { - _sharedWith = "@" + _sharedWith; // add atSign in front - } - if (!_isHidden) { - _isHidden = _keyName.startsWith("_"); - } - } - - public static Matcher createNamespaceQualifiedKeyNameMatcher(String s) { - return NAMESPACE_QUALIFIED_KEY_NAME.matcher(s); - } -} diff --git a/at_client/src/main/java/org/atsign/client/util/KeysUtil.java b/at_client/src/main/java/org/atsign/client/util/KeysUtil.java index 7c80a6a8..b5a1bd8d 100644 --- a/at_client/src/main/java/org/atsign/client/util/KeysUtil.java +++ b/at_client/src/main/java/org/atsign/client/util/KeysUtil.java @@ -12,13 +12,16 @@ import java.util.Map; import java.util.TreeMap; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; import org.atsign.client.api.AtKeys; import org.atsign.common.AtSign; +import org.atsign.common.Json; import org.atsign.common.exceptions.AtClientConfigException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import org.atsign.common.exceptions.AtDecryptionException; /** * Utility class for loading a saving {@link AtKeys} from the file system @@ -28,7 +31,7 @@ public class KeysUtil { static private final String EMPTY_IV = Base64.getEncoder().encodeToString(new byte[16]); - private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final ObjectMapper MAPPER = Json.MAPPER; private static final TypeReference> STRING_MAP_TYPE = new TypeReference<>() {}; @@ -80,7 +83,7 @@ public static AtKeys loadKeys(AtSign atSign) throws AtClientConfigException { public static AtKeys loadKeys(File file) throws AtClientConfigException { try { - return setAtKeysFromJson(new AtKeys(), Files.readString(file.toPath())); + return createAtKeysFromJson(Files.readString(file.toPath())); } catch (IOException e) { throw new AtClientConfigException("failed to read " + file, e); } @@ -141,30 +144,31 @@ private static String getAsJson(AtKeys keys) { } } - private static AtKeys setAtKeysFromJson(AtKeys keys, String json) { + private static AtKeys createAtKeysFromJson(String json) throws AtClientConfigException { try { Map map = MAPPER.readValue(json, STRING_MAP_TYPE); String version = map.getOrDefault(VERSION_KEY, VERSION_1); if (version.equals(VERSION_1)) { - setAtKeysVersion1(keys, map); + return createAtKeysVersion1(map); } else { - throw new RuntimeException("unsupported version of atKeys json : " + version); + throw new AtClientConfigException("unsupported version of AtKeys json : " + version); } - } catch (Exception e) { - throw new RuntimeException(e); + } catch (JsonProcessingException | AtDecryptionException e) { + throw new AtClientConfigException("failed to create AtKeys from json", e); } - return keys; } - private static void setAtKeysVersion1(AtKeys keys, Map map) throws Exception { - keys.setSelfEncryptKey(mapGet(map, SELF_ENCRYPT_KEY)); - keys.setEnrollmentId(mapGetEnrollmentId(map, ENROLLMENT_ID)); - keys.setApkamSymmetricKey(mapGet(map, APKAM_SYMMETRIC_KEY)); - - keys.setApkamPublicKey(mapGetDecrypted(map, PKAM_PUBLIC_KEY, keys.getSelfEncryptKey())); - keys.setApkamPrivateKey(mapGetDecrypted(map, PKAM_PRIVATE_KEY, keys.getSelfEncryptKey())); - keys.setEncryptPublicKey(mapGetDecrypted(map, ENCRYPT_PUBLIC_KEY, keys.getSelfEncryptKey())); - keys.setEncryptPrivateKey(mapGetDecrypted(map, ENCRYPT_PRIVATE_KEY, keys.getSelfEncryptKey())); + private static AtKeys createAtKeysVersion1(Map map) throws AtDecryptionException { + String selfEncryptKey = mapGet(map, SELF_ENCRYPT_KEY); + return AtKeys.builder() + .selfEncryptKey(selfEncryptKey) + .enrollmentId(mapGetEnrollmentId(map, ENROLLMENT_ID)) + .apkamSymmetricKey(mapGet(map, APKAM_SYMMETRIC_KEY)) + .apkamPublicKey(mapGetDecrypted(map, PKAM_PUBLIC_KEY, selfEncryptKey)) + .apkamPrivateKey(mapGetDecrypted(map, PKAM_PRIVATE_KEY, selfEncryptKey)) + .encryptPublicKey(mapGetDecrypted(map, ENCRYPT_PUBLIC_KEY, selfEncryptKey)) + .encryptPrivateKey(mapGetDecrypted(map, ENCRYPT_PRIVATE_KEY, selfEncryptKey)) + .build(); } private static void mapPut(Map map, String key, String value) { @@ -194,7 +198,8 @@ private static String mapGet(Map map, String key) { return map.get(key); } - private static String mapGetDecrypted(Map map, String key, String decryptKey) throws Exception { + private static String mapGetDecrypted(Map map, String key, String decryptKey) + throws AtDecryptionException { String value = map.get(key); return value != null ? aesDecryptFromBase64(value, decryptKey, EMPTY_IV) : null; } diff --git a/at_client/src/main/java/org/atsign/client/util/Preconditions.java b/at_client/src/main/java/org/atsign/client/util/Preconditions.java index 2447796a..7d07b074 100644 --- a/at_client/src/main/java/org/atsign/client/util/Preconditions.java +++ b/at_client/src/main/java/org/atsign/client/util/Preconditions.java @@ -2,6 +2,8 @@ import java.io.File; import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Basic precondition helpers (we could use something like guava but we want to limit dependencies) @@ -10,7 +12,7 @@ public class Preconditions { public static T checkNotNull(T instance, String message) { if (instance == null) { - throw new RuntimeException(message); + throw new IllegalArgumentException(message); } return instance; } @@ -19,6 +21,45 @@ public static T checkNotNull(T instance) { return checkNotNull(instance, "null"); } + public static void checkNull(Object instance, String message) { + if (instance != null) { + throw new IllegalArgumentException(message); + } + } + + public static void checkAllNull(String message, Object... instances) { + for (int i = 0; i < instances.length; i++) { + if (instances[i] != null) { + throw new IllegalArgumentException(message); + } + } + } + + public static Matcher checkMatches(String s, Pattern pattern, String message) { + Matcher matcher = pattern.matcher(s); + if (!matcher.matches()) { + throw new IllegalArgumentException(message); + } + return matcher; + } + + public static String checkNotBlank(String s) { + return checkNotBlank(s, "blank"); + } + + public static String checkNotBlank(String s, String message) { + if (s == null || s.isBlank()) { + throw new IllegalArgumentException(message); + } + return s; + } + + public static void checkTrue(boolean bool, String message) { + if (!bool) { + throw new IllegalArgumentException(message); + } + } + public static File checkFile(File f, Predicate predicate, String message) { if (!predicate.test(f)) { throw new IllegalArgumentException(message); diff --git a/at_client/src/main/java/org/atsign/client/util/RegisterUtil.java b/at_client/src/main/java/org/atsign/client/util/RegisterUtil.java index 12079e52..8b932cfd 100644 --- a/at_client/src/main/java/org/atsign/client/util/RegisterUtil.java +++ b/at_client/src/main/java/org/atsign/client/util/RegisterUtil.java @@ -18,6 +18,7 @@ import org.atsign.common.AtException; import org.atsign.common.AtSign; +import org.atsign.common.Json; import org.atsign.common.exceptions.AtRegistrarException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -26,7 +27,8 @@ * Utility class for obtaining a new {@link AtSign} */ public class RegisterUtil { - ObjectMapper objectMapper = new ObjectMapper(); + + ObjectMapper objectMapper = Json.MAPPER; /** * Calls API to get atsigns which are ready to be claimed. Returns a free atsign. diff --git a/at_client/src/main/java/org/atsign/common/AtSign.java b/at_client/src/main/java/org/atsign/common/AtSign.java index a335dfc7..2a3b1eea 100644 --- a/at_client/src/main/java/org/atsign/common/AtSign.java +++ b/at_client/src/main/java/org/atsign/common/AtSign.java @@ -1,65 +1,40 @@ package org.atsign.common; -/** - * The identity of people, systems and devices in the Atsign Platform - */ -public class AtSign { - public final String atSign; - private final String withoutPrefix; +import org.atsign.client.util.Preconditions; +import org.atsign.client.util.TypedString; - public AtSign(String atSign) { - if (atSign == null || atSign.trim().isEmpty()) { - throw new IllegalArgumentException("atSign may not be null or empty"); - } - this.atSign = formatAtSign(atSign); - - if ("@".equals(atSign)) { - throw new IllegalArgumentException("'" + atSign + "' is not a valid atSign"); - } +/** + * The identity of a person or system in the Atsign Platform + */ +public class AtSign extends TypedString { - this.withoutPrefix = this.atSign.substring(1); + public AtSign(String s) { + super(formatAtSign(s)); } public String withoutPrefix() { - return withoutPrefix; - } - - @Override - public String toString() { - return atSign; + return toString().substring(1); } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - AtSign atSign1 = (AtSign) o; - - return atSign.equals(atSign1.atSign); - } - - @Override - public int hashCode() { - return atSign.hashCode(); + /** + * Factory method + * + * @param s the string representation of the Atsign (can be with our without @ prefix) + * @return null is s is null or blank, otherwise the corresponding {@link AtSign} for s + */ + public static AtSign createAtSign(String s) { + return s != null && !s.isBlank() ? new AtSign(s) : null; } /** - * Returns a formatted atSign + * Returns a formatted atSign, ensuring that there is an @ prefix * - * @param atSignStr e.g. "@bob" - * @return formatted atSign (e.g. "alice " {@code -->} "@alice") + * @param s prefix or unprefixed atsign + * @return prefixed atsign */ - public static String formatAtSign(String atSignStr) { - atSignStr = atSignStr.trim(); - if (!atSignStr.startsWith("@")) { - atSignStr = "@" + atSignStr; - } - return atSignStr; + public static String formatAtSign(String s) { + String result = Preconditions.checkNotNull(s).trim(); + return result.startsWith("@") ? result : "@" + s; } - } diff --git a/at_client/src/main/java/org/atsign/common/Json.java b/at_client/src/main/java/org/atsign/common/Json.java new file mode 100644 index 00000000..658e83ad --- /dev/null +++ b/at_client/src/main/java/org/atsign/common/Json.java @@ -0,0 +1,52 @@ +package org.atsign.common; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; + +/** + * Utility class for JSON encoding / decoding + */ +public class Json { + + public static final ObjectMapper MAPPER = objectMapper(false); + + private static ObjectMapper objectMapper(boolean isStrict) { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, isStrict); + SimpleModule module = new SimpleModule(); + module.addDeserializer(OffsetDateTime.class, new AtStringDateTimeDeserializer()); + mapper.registerModule(module); + return mapper; + } + + /** + * Jackson serializer for DateTime strings + */ + private static class AtStringDateTimeDeserializer extends StdDeserializer { + + protected AtStringDateTimeDeserializer() { + super((Class) null); + } + + static final DateTimeFormatter dateTimeFormatter = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS'Z'").withZone(ZoneId.of("UTC")); + + @Override + public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { + String dateString = jsonParser.getText(); + ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateString, dateTimeFormatter); + return zonedDateTime.toOffsetDateTime(); + } + } +} diff --git a/at_client/src/main/java/org/atsign/common/KeyBuilders.java b/at_client/src/main/java/org/atsign/common/KeyBuilders.java deleted file mode 100644 index ec36c292..00000000 --- a/at_client/src/main/java/org/atsign/common/KeyBuilders.java +++ /dev/null @@ -1,321 +0,0 @@ -package org.atsign.common; - -import static org.atsign.common.Keys.AtKey; -import static org.atsign.common.Keys.PrivateHiddenKey; -import static org.atsign.common.Keys.PublicKey; -import static org.atsign.common.Keys.SelfKey; -import static org.atsign.common.Keys.SharedKey; -import static org.atsign.common.Keys.defaultAtSign; - -import org.atsign.common.exceptions.AtInvalidAtKeyException; - -/** - * Parent class for key builders - */ -@SuppressWarnings({"unused", "UnusedReturnValue"}) -public class KeyBuilders { - - /** - * Base interface for builders - */ - public interface KeyBuilder { - KeyBuilder timeToLive(int ttl); - - KeyBuilder timeToBirth(int ttb); - - Keys.AtKey build(); - - void validate() throws AtException; - - KeyBuilder namespace(String namespace); - } - - /** - * Base key builder - */ - public static abstract class BaseKeyBuilder { - AtKey _atKey; - - /// Set simple key without any namespace. For example "phone", "email" etc... - /// This is required. - public BaseKeyBuilder key(String key) { - key = key.trim(); - _atKey.name = key; - return this; - } - - /// Each app should write to a specific namespace. - /// This is required, unless the key already includes some '.' delimiters - public BaseKeyBuilder namespace(String namespace) { - namespace = namespace != null ? namespace.trim() : null; - _atKey.setNamespace(namespace); - return this; - } - - /// Set this value to set an expiry to a key in milliseconds. - /// Time until expiry - public BaseKeyBuilder timeToLive(int ttl) { - _atKey.metadata.ttl = ttl; - return this; - } - - /// Set this value to set time after which the key should be available in milliseconds. - /// Time until availability - public BaseKeyBuilder timeToBirth(int ttb) { - _atKey.metadata.ttb = ttb; - return this; - } - - /// Returns an instance of AtKey - public Keys.AtKey build() throws AtException { - // Validate if the data is set properly - validate(); - - return _atKey; - } - - /// Validates AtKey and throws Exception for a given issue - public void validate() throws AtException { - if (_atKey.name == null || _atKey.name.isEmpty()) { - throw new AtInvalidAtKeyException("Key cannot be empty"); - } - - // We only need to check namespace if the key already doesn't include a namespace - if (!_atKey.name.contains(".")) { - if (_atKey.getNamespace() == null || _atKey.getNamespace().isEmpty()) { - throw new AtInvalidAtKeyException("Namespace cannot be empty"); - } - } - } - } - - /** - * Builder class for cacheable keys. - */ - public static abstract class CachedKeyBuilder extends BaseKeyBuilder { - /** - *
    - *
  • Cacheable keys are cached on the recipient AtSign's secondary server when the - * ttr metadata value is set to a value greater than zero.
  • - *
  • TTR denotes the time to refresh the cached key. Accepts an integer value - * which represents the time units in seconds.
  • - *
  • CCD denotes the cascade delete. Accepts a boolean value. When set to true, deletes - * the cached key when corresponding key is deleted. When set to false, the cached key remains - * when corresponding key is deleted.
  • - *
- */ - public CachedKeyBuilder cache(int ttr, boolean ccd) { - _atKey.metadata.ttr = ttr; - _atKey.metadata.ccd = ccd; - _atKey.metadata.isCached = (ttr != 0); - return this; - } - } - - /** - * Builder for "public keys" in the AtSign Platform - */ - public static class PublicKeyBuilder extends CachedKeyBuilder implements KeyBuilder { - public PublicKeyBuilder() { - this(defaultAtSign); - } - - public PublicKeyBuilder(AtSign sharedBy) { - _atKey = new PublicKey(sharedBy); - _atKey.metadata.isPublic = true; - _atKey.metadata.isHidden = false; - } - - @Override - public PublicKeyBuilder key(String key) { - super.key(key); - return this; - } - - @Override - public PublicKeyBuilder namespace(String namespace) { - super.namespace(namespace); - return this; - } - - @Override - public PublicKeyBuilder timeToLive(int ttl) { - super.timeToLive(ttl); - return this; - } - - @Override - public PublicKeyBuilder timeToBirth(int ttb) { - super.timeToBirth(ttb); - return this; - } - - @Override - public PublicKey build() { - return (PublicKey) _atKey; - } - - @Override - public PublicKeyBuilder cache(int ttr, boolean ccd) { - super.cache(ttr, ccd); - return this; - } - } - - /** - * Builder for "shared keys" in the AtSign Platform - */ - public static class SharedKeyBuilder extends CachedKeyBuilder implements KeyBuilder { - public SharedKeyBuilder(AtSign sharedWith) { - this(defaultAtSign, sharedWith); - } - - public SharedKeyBuilder(AtSign sharedBy, AtSign sharedWith) { - _atKey = new SharedKey(sharedBy, sharedWith); - _atKey.metadata.isPublic = false; - _atKey.metadata.isHidden = false; - } - - /// Accepts a string which represents an atSign for the key is created. - void sharedWith(AtSign sharedWith) { - _atKey.sharedWith = sharedWith; - } - - @Override - public SharedKeyBuilder key(String key) { - super.key(key); - return this; - } - - @Override - public SharedKeyBuilder namespace(String namespace) { - super.namespace(namespace); - return this; - } - - @Override - public SharedKeyBuilder timeToLive(int ttl) { - super.timeToLive(ttl); - return this; - } - - @Override - public SharedKeyBuilder timeToBirth(int ttb) { - super.timeToBirth(ttb); - return this; - } - - @Override - public SharedKey build() { - return (SharedKey) _atKey; - } - - @Override - public SharedKeyBuilder cache(int ttr, boolean ccd) { - super.cache(ttr, ccd); - return this; - } - - @Override - public void validate() throws AtException { - //Call AbstractKeyBuilder validate method to perform the common validations. - super.validate(); - if (_atKey.sharedWith == null || _atKey.sharedWith.toString().isEmpty()) { - throw new AtInvalidAtKeyException("sharedWith cannot be empty"); - } - } - } - - /** - * Builder for "self keys" in the AtSign Platform - */ - public static class SelfKeyBuilder extends BaseKeyBuilder implements KeyBuilder { - public SelfKeyBuilder() { - this(defaultAtSign); - } - - public SelfKeyBuilder(AtSign sharedBy) { - this(sharedBy, null); - } - - public SelfKeyBuilder(AtSign sharedBy, AtSign sharedWith) { - _atKey = new SelfKey(sharedBy, sharedWith); - _atKey.metadata.isPublic = false; - _atKey.metadata.isHidden = false; - } - - @Override - public SelfKeyBuilder key(String key) { - super.key(key); - return this; - } - - @Override - public SelfKeyBuilder namespace(String namespace) { - super.namespace(namespace); - return this; - } - - @Override - public SelfKeyBuilder timeToLive(int ttl) { - super.timeToLive(ttl); - return this; - } - - @Override - public SelfKeyBuilder timeToBirth(int ttb) { - super.timeToBirth(ttb); - return this; - } - - @Override - public SelfKey build() { - return (SelfKey) _atKey; - } - } - - /** - * Builder for "private hidden keys" in the AtSign Platform - */ - public static class PrivateHiddenKeyBuilder extends BaseKeyBuilder { - public PrivateHiddenKeyBuilder() { - this(defaultAtSign); - } - - public PrivateHiddenKeyBuilder(AtSign sharedBy) { - _atKey = new PrivateHiddenKey(sharedBy); - _atKey.metadata.isHidden = true; - _atKey.metadata.isPublic = false; - } - - @Override - public PrivateHiddenKeyBuilder key(String key) { - super.key(key); - return this; - } - - @Override - public PrivateHiddenKeyBuilder namespace(String namespace) { - super.namespace(namespace); - return this; - } - - @Override - public PrivateHiddenKeyBuilder timeToLive(int ttl) { - super.timeToLive(ttl); - return this; - } - - @Override - public PrivateHiddenKeyBuilder timeToBirth(int ttb) { - super.timeToBirth(ttb); - return this; - } - - @Override - public PrivateHiddenKey build() { - return (PrivateHiddenKey) _atKey; - } - } - -} diff --git a/at_client/src/main/java/org/atsign/common/Keys.java b/at_client/src/main/java/org/atsign/common/Keys.java index 723510b3..1c88d0d7 100644 --- a/at_client/src/main/java/org/atsign/common/Keys.java +++ b/at_client/src/main/java/org/atsign/common/Keys.java @@ -1,72 +1,147 @@ package org.atsign.common; -import org.atsign.client.api.Secondary; -import org.atsign.client.util.KeyStringUtil; -import org.atsign.client.util.KeyStringUtil.KeyType; -import org.atsign.common.exceptions.MalformedKeyException; +import static org.atsign.client.util.Preconditions.*; +import static org.atsign.common.AtSign.createAtSign; +import static org.atsign.common.Metadata.*; -import com.fasterxml.jackson.core.JsonProcessingException; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import lombok.Builder; +import lombok.Getter; +import lombok.experimental.Accessors; /** * Parent class for key classes in the Atsign Platform */ @SuppressWarnings("unused") public abstract class Keys { - public static AtSign defaultAtSign; + + private static final Metadata PUBLIC_KEY_METADATA = Metadata.builder() + .isPublic(true) + .isHidden(false) + .isCached(false) + .isEncrypted(false) + .build(); + + private static final Metadata SELF_KEY_METADATA = Metadata.builder() + .isPublic(false) + .isHidden(false) + .isCached(false) + .isEncrypted(true) + .build(); + + private static final Metadata SHARED_KEY_METADATA = Metadata.builder() + .isPublic(false) + .isHidden(false) + .isCached(false) + .isEncrypted(true) + .build(); + + private static final Metadata PRIVATE_HIDDEN_KEY_METADATA = Metadata.builder() + .isPublic(false) + .isHidden(true) + .isCached(false) + .build(); + + private static final ThreadLocal>> RAW_KEY_PARSERS = + ThreadLocal.withInitial(() -> List.of(new SharedKeyRawKeyParser(), + new PrivateHiddenKeyRawKeyParser(), + new PublicKeyRawKeyParser(), + new SelfKeyRawKeyParser())); + + private static final Pattern ILLEGAL_KEY_CHARS = Pattern.compile("[@:\\s]"); + + private static final Pattern NAMESPACE_QUALIFIED_KEY_NAME = Pattern.compile("^(?!shared_key)(.+)\\.([^.]+)$"); + + public static Matcher createNamespaceQualifiedKeyNameMatcher(String s) { + return NAMESPACE_QUALIFIED_KEY_NAME.matcher(s); + } /** - * Base class for keys in the Atsign Platform + * Base class for keys in the Atsign Platform. */ + @Getter + @Accessors(fluent = true) public static abstract class AtKey { - public String name; - public AtSign sharedWith; - public AtSign sharedBy; - private String namespace; - public String getNamespace() { - return namespace; + private final String name; + private final AtSign sharedWith; + private final AtSign sharedBy; + private final AtomicReference metadata; + private final String rawKey; + + protected AtKey(AtSign sharedBy, AtSign sharedWith, String name, Metadata metadata, String rawKey) { + this.sharedBy = checkNotNull(sharedBy, "sharedBy is null"); + this.sharedWith = sharedWith; + this.name = checkKeyName(name); + this.metadata = new AtomicReference<>(checkMetadata(metadata)); + this.rawKey = rawKey; } - public void setNamespace(String namespace) { - if (namespace != null) { - while (namespace.startsWith(".")) { - namespace = namespace.substring(1); - } - namespace = namespace.trim(); - } - this.namespace = namespace; + protected AtKey(AtSign sharedBy, AtSign sharedWith, String name, Metadata metadata) { + this.sharedBy = checkNotNull(sharedBy, "sharedBy is null"); + this.sharedWith = sharedWith; + this.name = checkKeyName(name); + this.metadata = new AtomicReference<>(checkMetadata(metadata)); + this.rawKey = createRawKey(); } - public Metadata metadata; + public Metadata metadata() { + return metadata.get(); + } - // bool isRef = false; - public AtKey(AtSign sharedBy) { - if (sharedBy == null) { - throw new IllegalArgumentException("AtKey: sharedBy may not be null"); - } - this.sharedBy = sharedBy; - this.metadata = new Metadata(); + /** + * Sets any {@link Metadata} fields that are currently unset. + * + * @param metadata new values if those fields are unset in the key's current {@link Metadata}. + */ + public void updateMissingMetadata(Metadata metadata) { + this.metadata.set(checkMetadata(Metadata.merge(this.metadata.get(), metadata))); + } + + /** + * Updates {@link Metadata} fields with any non-null fields + * + * @param metadata new values (unset fields are ignored). + */ + public void overwriteMetadata(Metadata metadata) { + this.metadata.set(checkMetadata(Metadata.merge(metadata, this.metadata.get()))); } - public String getFullyQualifiedKeyName() { - return name + (namespace != null && !namespace.trim().isEmpty() ? "." + namespace : ""); + /** + * @return the namespace component of the name or null if no namespace. + */ + public String namespace() { + return getNamespace(name); + } + + /** + * + * @return the name without the namespace component. + */ + public String nameWithoutNamespace() { + return stripNamespace(name); } @Override public String toString() { - String s = ""; - if (metadata.isPublic) { - s += "public:"; - } else if (sharedWith != null) { - s += sharedWith + ":"; - } - s += getFullyQualifiedKeyName(); + return rawKey; + } - if (sharedBy != null) { - s += sharedBy; - } - return s; + protected String createRawKey() { + Metadata metadata = this.metadata.get(); + return new StringBuilder() + .append(isNotNullAndTrue(metadata.isCached()) ? "cached:" : "") + .append(isNotNullAndTrue(metadata.isPublic()) ? "public:" : "") + .append(sharedWith != null ? sharedWith + ":" : "") + .append(name) + .append(sharedBy) + .toString(); } } @@ -74,161 +149,320 @@ public String toString() { * Represents a "public key" in the Atsign Platform */ public static class PublicKey extends AtKey { - public PublicKey() { - this(defaultAtSign); + protected PublicKey(AtSign sharedBy, String name, Metadata metadata) { + super(sharedBy, null, name, metadata); } + } - public PublicKey(AtSign sharedBy) { - super(sharedBy); - super.metadata.isPublic = true; - super.metadata.isEncrypted = false; - super.metadata.isHidden = false; + @Builder(builderMethodName = "publicKeyBuilder", builderClassName = "PublicKeyBuilder") + public static PublicKey publicKey(AtSign sharedBy, String name, String namespace, Long ttl, Long ttb, Long ttr, + Boolean ccd, Boolean isCached, Boolean isBinary, Metadata metadata) { + if (metadata != null) { + checkAllNull("both metadata and metadata fields set, this is ambiguous and not supported", + ttl, ttb, ttr, ccd, isCached, isBinary); + } else { + MetadataBuilder builder = PUBLIC_KEY_METADATA.toBuilder(); + setTtlIfNotNull(builder, ttl); + setTtrIfNotNull(builder, ttr); + setTtbIfNotNull(builder, ttb); + setCcdIfNotNull(builder, ccd); + setIsCachedIfNotNull(builder, isCached); + setIsBinaryIfNotNull(builder, isBinary); + metadata = builder.build(); } + checkNotNull(sharedBy, "sharedBy is not set"); + checkNotNull(name, "name is not set"); + return new PublicKey(sharedBy, toName(name, namespace), metadata); } /** * Represents a "self key" in the Atsign Platform */ public static class SelfKey extends AtKey { - public SelfKey() { - this(defaultAtSign); - } - - public SelfKey(AtSign sharedBy) { - this(sharedBy, null); + protected SelfKey(AtSign sharedBy, AtSign sharedWith, String name, Metadata metadata) { + super(sharedBy, sharedWith, name, metadata); } + } - public SelfKey(AtSign sharedBy, AtSign sharedWith) { // possibility of `@bob:keyName@bob` - super(sharedBy); - super.sharedWith = sharedWith; - super.metadata.isPublic = false; - super.metadata.isEncrypted = true; - super.metadata.isHidden = false; + @Builder(builderMethodName = "selfKeyBuilder", builderClassName = "SelfKeyBuilder") + public static SelfKey selfKey(AtSign sharedBy, AtSign sharedWith, String name, String namespace, Long ttl, + Long ttb, Long ttr, Boolean ccd, Boolean isHidden, Boolean isBinary, + Metadata metadata) { + if (metadata != null) { + checkAllNull("both metadata and metadata fields set, this is ambiguous and not supported", + ttl, ttb, ttr, ccd, isBinary); + } else { + MetadataBuilder builder = SELF_KEY_METADATA.toBuilder(); + setTtlIfNotNull(builder, ttl); + setTtrIfNotNull(builder, ttr); + setTtbIfNotNull(builder, ttb); + setCcdIfNotNull(builder, ccd); + setIsHiddenIfNotNull(builder, isHidden); + setIsBinaryIfNotNull(builder, isBinary); + metadata = builder.build(); } + checkNotNull(sharedBy, "sharedBy is not set"); + checkNotNull(name, "name is not set"); + return new SelfKey(sharedBy, sharedWith, toName(name, namespace), metadata); } /** * Represents a "shared key" in the Atsign Platform */ public static class SharedKey extends AtKey { - public SharedKey(AtSign sharedBy, AtSign sharedWith) { - super(sharedBy); - if (sharedWith == null) { - throw new IllegalArgumentException("SharedKey: sharedWith may not be null"); + protected SharedKey(AtSign sharedBy, AtSign sharedWith, String name, Metadata metadata) { + super(sharedBy, checkNotNull(sharedWith), name, metadata); + } + } + + @Builder(builderMethodName = "sharedKeyBuilder", builderClassName = "SharedKeyBuilder") + public static SharedKey sharedKey(AtSign sharedBy, AtSign sharedWith, String name, String namespace, Long ttl, + Long ttb, Long ttr, Boolean ccd, Boolean isCached, Boolean isBinary, + Boolean isHidden, Metadata metadata, String rawKey) { + if (rawKey != null) { + checkAllNull("both rawKey and other fields set", + sharedBy, sharedWith, name, namespace, ttl, ttb, ttr, ccd, isCached, isBinary, isHidden); + SharedKeyRawKeyParser parser = new SharedKeyRawKeyParser(); + parser.test(rawKey); + return parser.parse(rawKey, metadata); + } + if (metadata != null) { + checkAllNull("both metadata and metadata fields set", + ttl, ttb, ttr, ccd, isCached, isBinary, isHidden); + } else { + MetadataBuilder builder = SHARED_KEY_METADATA.toBuilder(); + setTtlIfNotNull(builder, ttl); + setTtrIfNotNull(builder, ttr); + setTtbIfNotNull(builder, ttb); + setCcdIfNotNull(builder, ccd); + setIsCachedIfNotNull(builder, isCached); + setIsHiddenIfNotNull(builder, isHidden); + setIsBinaryIfNotNull(builder, isBinary); + metadata = builder.build(); + } + checkNotNull(sharedBy, "sharedBy is not set"); + checkNotNull(sharedWith, "sharedWith is not set"); + checkNotNull(name, "name is not set"); + return new SharedKey(sharedBy, sharedWith, toName(name, namespace), metadata); + } + + /** + * Represents a "private hidden key" in the Atsign Platform + */ + public static class PrivateHiddenKey extends AtKey { + protected PrivateHiddenKey(AtSign sharedBy, String name, Metadata metadata, String rawKey) { + super(sharedBy, null, name, metadata, checkNotNull(rawKey)); + } + } + + @Builder(builderMethodName = "privateHiddenKeyBuilder", builderClassName = "PrivateHiddenKeyBuilder") + public static PrivateHiddenKey privateHiddenKey(AtSign sharedBy, String name, String namespace, Metadata metadata, + String rawKey) { + metadata = metadata != null ? metadata : PRIVATE_HIDDEN_KEY_METADATA; + checkNotNull(sharedBy, "sharedBy is not set"); + checkNotNull(name, "name is not set"); + return new PrivateHiddenKey(sharedBy, toName(name, namespace), metadata, rawKey); + } + + @Builder(builderMethodName = "keyBuilder", builderClassName = "KeyBuilder") + public static AtKey key(String rawKey, Metadata metadata) { + for (RawKeyParser parser : RAW_KEY_PARSERS.get()) { + if (parser.test(rawKey)) { + return parser.parse(rawKey, metadata); + } + } + throw new IllegalArgumentException(rawKey + " does NOT match any raw key parser"); + } + + private interface RawKeyParser extends Predicate { + T parse(String rawKey, Metadata metadata); + } + + private static class PublicKeyRawKeyParser implements RawKeyParser { + + private final Pattern PATTERN = Pattern.compile("(cached:)?public:([^@]+)(@.+)"); + + private Matcher matcher; + + public boolean test(String rawKey) { + matcher = PATTERN.matcher(rawKey); + if (matcher.matches()) { + return isLegalKeyName(matcher.group(2)); + } else { + return false; } - super.sharedWith = sharedWith; - super.metadata.isPublic = false; - super.metadata.isEncrypted = true; - super.metadata.isHidden = false; } - public static SharedKey fromString(String key) throws IllegalArgumentException { - if (key == null) { - throw new IllegalArgumentException("SharedKey.fromString(key) : key may not be null"); + public PublicKey parse(String rawKey, Metadata metadata) { + checkNotNull(matcher, "matcher does not match"); + MetadataBuilder metadataBuilder = PUBLIC_KEY_METADATA.toBuilder(); + if (metadata != null) { + metadataBuilder = Metadata.merge(metadata, metadataBuilder.build()).toBuilder(); } - String[] splitByColon = key.split(":"); - if (splitByColon.length != 2) { - throw new IllegalArgumentException( - "SharedKey.fromString('" + key + "') : key must have structure @bob:foo.bar@alice"); + if (matcher.group(1) != null) { + metadataBuilder.isCached(true); } - String sharedWith = splitByColon[0]; - String[] splitByAtSign = splitByColon[1].split("@"); - if (splitByAtSign.length != 2) { - throw new IllegalArgumentException( - "SharedKey.fromString('" + key + "') : key must have structure @bob:foo.bar@alice"); + if (matcher.group(2).startsWith("_")) { + metadataBuilder.isHidden(true); } - String keyName = splitByAtSign[0]; - String sharedBy = splitByAtSign[1]; - SharedKey sharedKey = new SharedKey(new AtSign(sharedBy), new AtSign(sharedWith)); - sharedKey.name = keyName; - return sharedKey; + return publicKeyBuilder() + .name(matcher.group(2)) + .sharedBy(createAtSign(matcher.group(3))) + .metadata(metadataBuilder.build()) + .build(); } + } + + private static class SelfKeyRawKeyParser implements RawKeyParser { + + private static final Pattern PATTERN = Pattern.compile("(@[^:]+:)?([^@]+)(@.+)"); - public String getSharedSharedKeyName() { - return sharedWith + ":shared_key" + sharedBy; + private Matcher matcher; + + public boolean test(String rawKey) { + matcher = PATTERN.matcher(rawKey); + if (matcher.matches()) { + return (matcher.group(1) == null || chop(matcher.group(1)).equals(matcher.group(3))) + && isLegalKeyName(matcher.group(2)); + } else { + return false; + } + } + + public SelfKey parse(String rawKey, Metadata metadata) { + checkNotNull(matcher, "matcher does not match"); + MetadataBuilder metadataBuilder = SELF_KEY_METADATA.toBuilder(); + if (metadata != null) { + metadataBuilder = Metadata.merge(metadata, metadataBuilder.build()).toBuilder(); + } + if (matcher.group(2).startsWith("_")) { + metadataBuilder.isHidden(true); + } + return selfKeyBuilder() + .sharedWith(createAtSign(chop(matcher.group(1)))) + .name(matcher.group(2)) + .sharedBy(createAtSign(matcher.group(3))) + .metadata(metadataBuilder.build()) + .build(); } } - /** - * Represents a "private hidden key" in the Atsign Platform - */ - public static class PrivateHiddenKey extends AtKey { - public PrivateHiddenKey() { - this(defaultAtSign); + private static class SharedKeyRawKeyParser implements RawKeyParser { + + private static final Pattern PATTERN = Pattern.compile("(cached:)?(@[^:]+):([^@]+)(@.+)"); + + private Matcher matcher; + + public boolean test(String rawKey) { + matcher = PATTERN.matcher(rawKey); + if (matcher.matches()) { + return !matcher.group(2).equals(matcher.group(4)); + } else { + return false; + } } - public PrivateHiddenKey(AtSign sharedBy) { - super(sharedBy); - super.metadata = new Metadata(); + public SharedKey parse(String rawKey, Metadata metadata) { + checkNotNull(matcher, "matcher does not match"); + MetadataBuilder metadataBuilder = SHARED_KEY_METADATA.toBuilder(); + if (metadata != null) { + metadataBuilder = Metadata.merge(metadata, metadataBuilder.build()).toBuilder(); + } + if (matcher.group(1) != null) { + metadataBuilder.isCached(true); + } + if (matcher.group(3).startsWith("_")) { + metadataBuilder.isHidden(true); + } + return sharedKeyBuilder() + .sharedWith(createAtSign(matcher.group(2))) + .name(matcher.group(3)) + .sharedBy(createAtSign(matcher.group(4))) + .metadata(metadataBuilder.build()) + .build(); } } - /** - * Generate an AtKey object from a given full key name. - * - * @param fullAtKeyName eg: @bob:phone@alice - * @return AtKey object - * @throws AtException if key string doesn't match recognized structure - */ - @SuppressWarnings("JavaDoc") - public static AtKey fromString(String fullAtKeyName) throws AtException { - KeyStringUtil keyStringUtil = new KeyStringUtil(fullAtKeyName); - KeyType keyType = keyStringUtil.getKeyType(); - String keyName = keyStringUtil.getKeyName(); - AtSign sharedBy = new AtSign(keyStringUtil.getSharedBy()); - AtSign sharedWith = null; - if (keyStringUtil.getSharedWith() != null) { - sharedWith = new AtSign(keyStringUtil.getSharedWith()); - } - String namespace = keyStringUtil.getNamespace(); - boolean isCached = keyStringUtil.isCached(); - boolean isHidden = keyStringUtil.isHidden(); - AtKey atKey; - switch (keyType) { - case PUBLIC_KEY: - atKey = new KeyBuilders.PublicKeyBuilder(sharedBy).key(keyName).build(); - break; - case SHARED_KEY: - atKey = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key(keyName).build(); - break; - case SELF_KEY: - atKey = new KeyBuilders.SelfKeyBuilder(sharedBy, sharedWith).key(keyName).build(); - break; - case PRIVATE_HIDDEN_KEY: - atKey = new KeyBuilders.PrivateHiddenKeyBuilder(sharedBy).key(keyName).build(); - break; - default: - throw new MalformedKeyException("Could not find KeyType for Key \"" + fullAtKeyName); - } - atKey.setNamespace(namespace); - atKey.metadata.isCached = isCached; - if (!atKey.metadata.isHidden) { - atKey.metadata.isHidden = isHidden; // if KeyBuilders constructor did not already evaluate isHidden, then do it - // here - } - return atKey; + private static class PrivateHiddenKeyRawKeyParser implements RawKeyParser { + + private static final Pattern PATTERN = Pattern.compile("(private|privatekey):([^@]+)(@.+)"); + + private Matcher matcher; + + public boolean test(String rawKey) { + matcher = PATTERN.matcher(rawKey); + return matcher.matches(); + } + + public PrivateHiddenKey parse(String rawKey, Metadata metadata) { + checkNotNull(matcher, "matcher does not match"); + MetadataBuilder metadataBuilder; + if (metadata != null) { + metadataBuilder = Metadata.merge(metadata, PRIVATE_HIDDEN_KEY_METADATA).toBuilder(); + } else { + metadataBuilder = PRIVATE_HIDDEN_KEY_METADATA.toBuilder(); + } + return privateHiddenKeyBuilder() + .name(matcher.group(2)) + .sharedBy(createAtSign(matcher.group(3))) + .metadata(metadataBuilder.build()) + .rawKey(rawKey) + .build(); + } } - /** - * Generate an {@link AtKey} whose metadata is populated from the given `llookup:meta:key` - * response. - * - * @param fullAtKeyName The full AtKey name, eg: `@bob:phone@alice` - * @param metadataResponse `llookup:meta:key` rawResponse from secondary server - * @return AtKey whose metadata is populated from the llookup:meta:key rawResponse from - * secondary server - * @throws AtException if key string doesn't match recognized structure - * @throws JsonProcessingException if metadataResponse is invalid JSON - */ - @SuppressWarnings("JavaDoc") - public static AtKey fromString(String fullAtKeyName, Secondary.Response metadataResponse) - throws AtException, JsonProcessingException { - AtKey atKey = fromString(fullAtKeyName); - atKey.metadata = Metadata.squash(atKey.metadata, Metadata.fromJson(metadataResponse.getRawDataResponse())); - return atKey; + private static String toName(String name, String namespace) { + if (namespace != null && !namespace.isBlank()) { + return name + "." + namespace; + } else { + return name; + } + } + + protected static String getNamespace(String name) { + Matcher matcher = createNamespaceQualifiedKeyNameMatcher(name); + if (matcher.matches()) { + return matcher.group(2); + } else { + return null; + } + } + + protected static String stripNamespace(String name) { + Matcher matcher = createNamespaceQualifiedKeyNameMatcher(name); + if (matcher.matches()) { + return matcher.group(1); + } else { + return name; + } + } + + private static String checkKeyName(String s) { + checkNotBlank(s, "key name is blank"); + if (!isLegalKeyName(s)) { + throw new IllegalArgumentException("illegal characters in key name"); + } + return s; + } + + private static boolean isLegalKeyName(String s) { + return !ILLEGAL_KEY_CHARS.matcher(s).find(); + } + + private static String chop(String s) { + return s != null ? s.substring(0, s.length() - 1) : null; } - public static AtKey fromString(Secondary.Response lookupAllResponse) { - throw new RuntimeException("Not implemented"); + private static Metadata checkMetadata(Metadata metadata) { + checkNotNull(metadata, "metadata is null"); + checkTrue(metadata.ttl() == null || metadata.ttl() >= 0, "ttl cannot be negative"); + checkTrue(metadata.ttb() == null || metadata.ttb() >= 0, "ttb cannot be negative"); + checkTrue(metadata.ttr() == null || metadata.ttr() >= -1, "ttr cannot be < -1"); + return metadata; } + + private static boolean isNotNullAndTrue(Boolean b) { + return b != null ? b : false; + } + } diff --git a/at_client/src/main/java/org/atsign/common/Metadata.java b/at_client/src/main/java/org/atsign/common/Metadata.java index f622f432..6895fe6a 100644 --- a/at_client/src/main/java/org/atsign/common/Metadata.java +++ b/at_client/src/main/java/org/atsign/common/Metadata.java @@ -1,277 +1,356 @@ package org.atsign.common; -import java.io.IOException; import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import lombok.Builder; +import lombok.Value; +import lombok.experimental.Accessors; +import lombok.extern.jackson.Jacksonized; /** - * Data class which models key metadata in the Atsign Platform + * Value class which models key metadata in the Atsign Platform */ -@JsonIgnoreProperties(ignoreUnknown = true) +@Value +@Builder(toBuilder = true) +@Accessors(fluent = true) +@Jacksonized public class Metadata { - static final ObjectMapper mapper = new ObjectMapper(); - - public Integer ttl; - public Integer ttb; - public Integer ttr; - public Boolean ccd; - public String createdBy; - public String updatedBy; - @JsonDeserialize(using = AtStringDateTimeDeserializer.class) - public OffsetDateTime availableAt; - @JsonDeserialize(using = AtStringDateTimeDeserializer.class) - public OffsetDateTime expiresAt; - @JsonDeserialize(using = AtStringDateTimeDeserializer.class) - public OffsetDateTime refreshAt; - @JsonDeserialize(using = AtStringDateTimeDeserializer.class) - public OffsetDateTime createdAt; - @JsonDeserialize(using = AtStringDateTimeDeserializer.class) - public OffsetDateTime updatedAt; - public String status; - public Integer version; - public String dataSignature; - public String sharedKeyStatus; - public Boolean isPublic = false; - public Boolean isEncrypted = true; - public Boolean isHidden = false; - public Boolean namespaceAware = true; - public Boolean isBinary = false; - public Boolean isCached = false; - public String sharedKeyEnc; - public String pubKeyCS; - public String encoding; - public String ivNonce; + + Long ttl; + Long ttb; + Long ttr; + Boolean ccd; + String createdBy; + String updatedBy; + OffsetDateTime availableAt; + OffsetDateTime expiresAt; + OffsetDateTime refreshAt; + OffsetDateTime createdAt; + OffsetDateTime updatedAt; + String status; + Integer version; + String dataSignature; + String sharedKeyStatus; + Boolean isPublic; + Boolean isEncrypted; + Boolean isHidden; + Boolean namespaceAware; + Boolean isBinary; + Boolean isCached; + String sharedKeyEnc; + String pubKeyCS; + String encoding; + String ivNonce; + + // required for successful javadoc + + /** + * A builder for instantiating {@link Metadata} instances. Note: Metadata is immutable so if you + * want create a modified instance then use the toBuilder() method, override the fields and invoke + * build(). + */ + public static class MetadataBuilder { + }; public static Metadata fromJson(String json) throws JsonProcessingException { - return mapper.readValue(json, Metadata.class); + return Json.MAPPER.readValue(json, Metadata.class); } @Override public String toString() { - String s = ""; - if (ttl != null) { - s += ":ttl:" + ttl; + return new StringBuilder() + .append(ttl != null ? ":ttl:" + ttl : "") + .append(ttb != null ? ":ttb:" + ttb : "") + .append(ttr != null ? ":ttr:" + ttr : "") + .append(ccd != null ? ":ccd:" + ccd : "") + .append(dataSignature != null ? ":dataSignature:" + dataSignature : "") + .append(sharedKeyStatus != null ? ":sharedKeyStatus:" + sharedKeyStatus : "") + .append(sharedKeyEnc != null ? ":sharedKeyEnc:" + sharedKeyEnc : "") + .append(pubKeyCS != null ? ":pubKeyCS:" + pubKeyCS : "") + .append(isBinary != null ? ":isBinary:" + isBinary : "") + .append(isEncrypted != null ? ":isEncrypted:" + isEncrypted : "") + .append(encoding != null ? ":encoding:" + encoding : "") + .append(ivNonce != null ? ":ivNonce:" + ivNonce : "") + .toString(); + } + + /** + * Combines two metadata instances into a new metadata instance + * + * @param md1 has priority + * @param md2 use fields from here if not in md1 + * @return A new merged metadata instance + */ + public static Metadata merge(Metadata md1, Metadata md2) { + return toMergedBuilder(md1, md2).build(); + } + + /** + * Creates a builder for {@link Metadata} based on the combined fields of two + * Metadata instances. + * + * @param md1 has priority + * @param md2 use fields from here if not in md1 + * @return a builder with the squashed fields + */ + public static MetadataBuilder toMergedBuilder(Metadata md1, Metadata md2) { + MetadataBuilder builder = new MetadataBuilder(); + + if (!setIsPublicIfNotNull(builder, md1.isPublic)) { + setIsPublicIfNotNull(builder, md2.isPublic); } - if (ttb != null) { - s += ":ttb:" + ttb; + if (!setIsHiddenIfNotNull(builder, md1.isHidden)) { + setIsHiddenIfNotNull(builder, md2.isHidden); } - if (ttr != null) { - s += ":ttr:" + ttr; + if (!setIsCachedIfNotNull(builder, md1.isCached)) { + setIsCachedIfNotNull(builder, md2.isCached); } - if (ccd != null) { - s += ":ccd:" + ccd; + if (!setTtlIfNotNull(builder, md1.ttl)) { + setTtlIfNotNull(builder, md2.ttl); } - if (dataSignature != null) { - s += ":dataSignature:" + dataSignature; + if (!setTtbIfNotNull(builder, md1.ttb)) { + setTtbIfNotNull(builder, md2.ttb); } - if (sharedKeyStatus != null) { - s += ":sharedKeyStatus:" + sharedKeyStatus; + if (!setTtrIfNotNull(builder, md1.ttr)) { + setTtrIfNotNull(builder, md2.ttr); } - if (sharedKeyEnc != null) { - s += ":sharedKeyEnc:" + sharedKeyEnc; + if (!setCcdIfNotNull(builder, md1.ccd)) { + setCcdIfNotNull(builder, md2.ccd); } - if (pubKeyCS != null) { - s += ":pubKeyCS:" + pubKeyCS; + if (!setAvailableAtIfNotNull(builder, md1.availableAt)) { + setAvailableAtIfNotNull(builder, md2.availableAt); } - if (isBinary != null) { - s += ":isBinary:" + isBinary; + if (!setExpiresAtIfNotNull(builder, md1.expiresAt)) { + setExpiresAtIfNotNull(builder, md2.expiresAt); } - if (isEncrypted != null) { - s += ":isEncrypted:" + isEncrypted; + if (!setRefreshAtIfNotNull(builder, md1.refreshAt)) { + setRefreshAtIfNotNull(builder, md2.refreshAt); } - if (encoding != null) { - s += ":encoding:" + encoding; + if (!setCreatedAtIfNotNull(builder, md1.createdAt)) { + setCreatedAtIfNotNull(builder, md2.createdAt); } - if (ivNonce != null && !ivNonce.isEmpty()) { - s += ":ivNonce:" + ivNonce; + if (!setUpdatedAtIfNotNull(builder, md1.updatedAt)) { + setUpdatedAtIfNotNull(builder, md2.updatedAt); } - return s; - } - - /** - * Squashes the two metadatas into one metadata. - * - * @param firstMetadata has priority - * @param secondMetadata use fields from here if not in firstMetadata - * @return One merged metadata object - */ - public static Metadata squash(Metadata firstMetadata, Metadata secondMetadata) { - Metadata metadata = new Metadata(); - if (firstMetadata.ttl != null) { - metadata.ttl = firstMetadata.ttl; - } else if (secondMetadata.ttl != null) { - metadata.ttl = secondMetadata.ttl; + if (!setDataSignatureIfNotNull(builder, md1.dataSignature)) { + setDataSignatureIfNotNull(builder, md2.dataSignature); } - - if (firstMetadata.ttb != null) { - metadata.ttb = firstMetadata.ttb; - } else if (secondMetadata.ttb != null) { - metadata.ttb = secondMetadata.ttb; + if (!setSharedKeyStatusIfNotNull(builder, md1.sharedKeyStatus)) { + setSharedKeyStatusIfNotNull(builder, md2.sharedKeyStatus); } - - if (firstMetadata.ttr != null) { - metadata.ttr = firstMetadata.ttr; - } else if (secondMetadata.ttr != null) { - metadata.ttr = secondMetadata.ttr; + if (!setSharedKeyEncIfNotNull(builder, md1.sharedKeyEnc)) { + setSharedKeyEncIfNotNull(builder, md2.sharedKeyEnc); } - - if (firstMetadata.ccd != null) { - metadata.ccd = firstMetadata.ccd; - } else if (secondMetadata.ccd != null) { - metadata.ccd = secondMetadata.ccd; + if (!setIsEncryptedIfNotNull(builder, md1.isEncrypted)) { + setIsEncryptedIfNotNull(builder, md2.isEncrypted); } - - if (firstMetadata.availableAt != null) { - metadata.availableAt = firstMetadata.availableAt; - } else if (secondMetadata.availableAt != null) { - metadata.availableAt = secondMetadata.availableAt; + if (!setNamespaceAwareIfNotNull(builder, md1.namespaceAware)) { + setNamespaceAwareIfNotNull(builder, md2.namespaceAware); } - - if (firstMetadata.expiresAt != null) { - metadata.expiresAt = firstMetadata.expiresAt; - } else if (secondMetadata.expiresAt != null) { - metadata.expiresAt = secondMetadata.expiresAt; + if (!setIsBinaryIfNotNull(builder, md1.isBinary)) { + setIsBinaryIfNotNull(builder, md2.isBinary); } - - if (firstMetadata.refreshAt != null) { - metadata.refreshAt = firstMetadata.refreshAt; - } else if (secondMetadata.refreshAt != null) { - metadata.refreshAt = secondMetadata.refreshAt; + if (!setPubKeyCSIfNotNull(builder, md1.pubKeyCS)) { + setPubKeyCSIfNotNull(builder, md2.pubKeyCS); } - - if (firstMetadata.createdAt != null) { - metadata.createdAt = firstMetadata.createdAt; - } else if (secondMetadata.createdAt != null) { - metadata.createdAt = secondMetadata.createdAt; + if (!setEncodingIfNotNull(builder, md1.encoding)) { + setEncodingIfNotNull(builder, md2.encoding); } + if (!setIvNonceIfNotNull(builder, md1.ivNonce)) { + setIvNonceIfNotNull(builder, md2.ivNonce); + } + + return builder; + } - if (firstMetadata.updatedAt != null) { - metadata.updatedAt = firstMetadata.updatedAt; - } else if (secondMetadata.updatedAt != null) { - metadata.updatedAt = secondMetadata.updatedAt; + public static boolean setIsHiddenIfNotNull(MetadataBuilder builder, Boolean value) { + if (value != null) { + builder.isHidden(value); + return true; + } else { + return false; } + } - if (firstMetadata.dataSignature != null) { - metadata.dataSignature = firstMetadata.dataSignature; - } else if (secondMetadata.dataSignature != null) { - metadata.dataSignature = secondMetadata.dataSignature; + public static boolean setIsPublicIfNotNull(MetadataBuilder builder, Boolean value) { + if (value != null) { + builder.isPublic(value); + return true; + } else { + return false; } + } - if (firstMetadata.sharedKeyStatus != null) { - metadata.sharedKeyStatus = firstMetadata.sharedKeyStatus; - } else if (secondMetadata.sharedKeyStatus != null) { - metadata.sharedKeyStatus = secondMetadata.sharedKeyStatus; + public static boolean setIsCachedIfNotNull(MetadataBuilder builder, Boolean value) { + if (value != null) { + builder.isCached(value); + return true; + } else { + return false; } + } - if (firstMetadata.sharedKeyEnc != null) { - metadata.sharedKeyEnc = firstMetadata.sharedKeyEnc; - } else if (secondMetadata.sharedKeyEnc != null) { - metadata.sharedKeyEnc = secondMetadata.sharedKeyEnc; + public static boolean setTtlIfNotNull(MetadataBuilder builder, Long value) { + if (value != null) { + builder.ttl(value); + return true; + } else { + return false; } + } - if (firstMetadata.isPublic != null) { - metadata.isPublic = firstMetadata.isPublic; - } else if (secondMetadata.isPublic != null) { - metadata.isPublic = secondMetadata.isPublic; + public static boolean setTtbIfNotNull(MetadataBuilder builder, Long value) { + if (value != null) { + builder.ttb(value); + return true; + } else { + return false; } + } - if (firstMetadata.isEncrypted != null) { - metadata.isEncrypted = firstMetadata.isEncrypted; - } else if (secondMetadata.isEncrypted != null) { - metadata.isEncrypted = secondMetadata.isEncrypted; + public static boolean setTtrIfNotNull(MetadataBuilder builder, Long value) { + if (value != null) { + builder.ttr(value); + return true; + } else { + return false; } + } - if (firstMetadata.isHidden != null) { - metadata.isHidden = firstMetadata.isHidden; - } else if (secondMetadata.isHidden != null) { - metadata.isHidden = secondMetadata.isHidden; + public static boolean setCcdIfNotNull(MetadataBuilder builder, Boolean value) { + if (value != null) { + builder.ccd(value); + return true; + } else { + return false; } + } - if (firstMetadata.namespaceAware != null) { - metadata.namespaceAware = firstMetadata.namespaceAware; - } else if (secondMetadata.namespaceAware != null) { - metadata.namespaceAware = secondMetadata.namespaceAware; + private static boolean setAvailableAtIfNotNull(MetadataBuilder builder, OffsetDateTime value) { + if (value != null) { + builder.availableAt(value); + return true; + } else { + return false; } + } - if (firstMetadata.isBinary != null) { - metadata.isBinary = firstMetadata.isBinary; - } else if (secondMetadata.isBinary != null) { - metadata.isBinary = secondMetadata.isBinary; + private static boolean setExpiresAtIfNotNull(MetadataBuilder builder, OffsetDateTime value) { + if (value != null) { + builder.expiresAt(value); + return true; + } else { + return false; } + } - if (firstMetadata.isCached != null) { - metadata.isCached = firstMetadata.isCached; - } else if (secondMetadata.isCached != null) { - metadata.isCached = secondMetadata.isCached; + private static boolean setRefreshAtIfNotNull(MetadataBuilder builder, OffsetDateTime value) { + if (value != null) { + builder.refreshAt(value); + return true; + } else { + return false; } + } - if (firstMetadata.sharedKeyEnc != null) { - metadata.sharedKeyEnc = firstMetadata.sharedKeyEnc; - } else if (secondMetadata.sharedKeyEnc != null) { - metadata.sharedKeyEnc = secondMetadata.sharedKeyEnc; + private static boolean setCreatedAtIfNotNull(MetadataBuilder builder, OffsetDateTime value) { + if (value != null) { + builder.createdAt(value); + return true; + } else { + return false; } + } - if (firstMetadata.pubKeyCS != null) { - metadata.pubKeyCS = firstMetadata.pubKeyCS; - } else if (secondMetadata.pubKeyCS != null) { - metadata.pubKeyCS = secondMetadata.pubKeyCS; + private static boolean setUpdatedAtIfNotNull(MetadataBuilder builder, OffsetDateTime value) { + if (value != null) { + builder.updatedAt(value); + return true; + } else { + return false; } + } - if (firstMetadata.encoding != null) { - metadata.encoding = firstMetadata.encoding; - } else if (secondMetadata.encoding != null) { - metadata.encoding = secondMetadata.encoding; + public static boolean setIsBinaryIfNotNull(MetadataBuilder builder, Boolean value) { + if (value != null) { + builder.isBinary(value); + return true; + } else { + return false; } - if (firstMetadata.ivNonce != null) { - metadata.ivNonce = firstMetadata.ivNonce; - } else if (secondMetadata.ivNonce != null) { - metadata.ivNonce = secondMetadata.ivNonce; + } + + public static boolean setIsEncryptedIfNotNull(MetadataBuilder builder, Boolean value) { + if (value != null) { + builder.isEncrypted(value); + return true; + } else { + return false; } + } - return metadata; + public static boolean setDataSignatureIfNotNull(MetadataBuilder builder, String value) { + if (value != null) { + builder.dataSignature(value); + return true; + } else { + return false; + } } - /** - * Jackson serializer for DateTime strings - */ - @SuppressWarnings("unused") - public static class AtStringDateTimeDeserializer extends StdDeserializer { - public AtStringDateTimeDeserializer() { - this((Class) null); + public static boolean setNamespaceAwareIfNotNull(MetadataBuilder builder, Boolean value) { + if (value != null) { + builder.namespaceAware(value); + return true; + } else { + return false; } + } - protected AtStringDateTimeDeserializer(Class vc) { - super(vc); + public static boolean setSharedKeyStatusIfNotNull(MetadataBuilder builder, String value) { + if (value != null) { + builder.sharedKeyStatus(value); + return true; + } else { + return false; } + } - protected AtStringDateTimeDeserializer(JavaType valueType) { - super(valueType); + public static boolean setSharedKeyEncIfNotNull(MetadataBuilder builder, String value) { + if (value != null) { + builder.sharedKeyEnc(value); + return true; + } else { + return false; } + } - protected AtStringDateTimeDeserializer(StdDeserializer src) { - super(src); + public static boolean setPubKeyCSIfNotNull(MetadataBuilder builder, String value) { + if (value != null) { + builder.pubKeyCS(value); + return true; + } else { + return false; } + } - static final DateTimeFormatter dateTimeFormatter = - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS'Z'").withZone(ZoneId.of("UTC")); + public static boolean setEncodingIfNotNull(MetadataBuilder builder, String value) { + if (value != null) { + builder.encoding(value); + return true; + } else { + return false; + } + } - @Override - public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) - throws IOException { - String dateString = jsonParser.getText(); - ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateString, dateTimeFormatter); - return zonedDateTime.toOffsetDateTime(); + public static boolean setIvNonceIfNotNull(MetadataBuilder builder, String value) { + if (value != null) { + builder.ivNonce(value); + return true; + } else { + return false; } } } diff --git a/at_client/src/main/java/org/atsign/common/ResponseTransformers.java b/at_client/src/main/java/org/atsign/common/ResponseTransformers.java index 4bd9c824..c546c085 100644 --- a/at_client/src/main/java/org/atsign/common/ResponseTransformers.java +++ b/at_client/src/main/java/org/atsign/common/ResponseTransformers.java @@ -15,7 +15,8 @@ */ @Slf4j public class ResponseTransformers { - static final ObjectMapper mapper = new ObjectMapper(); + + static final ObjectMapper mapper = Json.MAPPER; /** * Transformer for scan command responses diff --git a/at_client/src/main/java/org/atsign/common/VerbBuilders.java b/at_client/src/main/java/org/atsign/common/VerbBuilders.java index a585c8b1..9da7ef66 100644 --- a/at_client/src/main/java/org/atsign/common/VerbBuilders.java +++ b/at_client/src/main/java/org/atsign/common/VerbBuilders.java @@ -1,799 +1,722 @@ package org.atsign.common; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static org.atsign.client.util.Preconditions.*; +import static org.atsign.client.util.StringUtil.isBlank; +import static org.atsign.common.Metadata.*; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.atsign.client.util.EnrollmentId; +import org.atsign.client.util.TypedString; import org.atsign.common.Keys.AtKey; -import org.atsign.common.Keys.PublicKey; -import org.atsign.common.Keys.SharedKey; +import org.atsign.common.Metadata.MetadataBuilder; -import static org.atsign.client.util.StringUtil.isBlank; +import com.fasterxml.jackson.core.JsonProcessingException; + +import lombok.Builder; /** * - * Parent class for builders that build commands that are accepted by a secondary server + * Contains builders for composing Atsign protocol command strings * */ public class VerbBuilders { + private static final Metadata EMPTY_METADATA = Metadata.builder().build(); + /** - * Base command interface + * A builder to compose an Atsign protocol command with the from verb. The from verb + * is used to tell the Atsign server whom you claim to be and initiates the authentication workflow. + * + * @param atSign The {@link AtSign} you claim to be + * @return A correctly formed from verb command + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. */ - public interface VerbBuilder { - /// Build the command to be sent to remote secondary for execution. - String build(); + @Builder(builderMethodName = "fromCommandBuilder", builderClassName = "FromCommandBuilder") + public static String from(AtSign atSign) { + checkNotNull(atSign, "atSign not set"); + return "from:" + atSign; } /** - * Atsign Platform from command builder. - * This initiates authentication + * A builder to compose an Atsign protocol command with the cram verb. The cram verb + * is used to boostrap authenticate one's own self as an owner of the Atsign server. It is intended + * to be used once until a set of PKAM keys are cut on the owner's mobile device and from then on we + * use the pkam verb. + * + * @param digest the challenge sent as a result of the from verb encrypted with the CRAM key/secret + * @return A correctly formed cram verb command. + * @throws IllegalArgumentException If mandatory fields are not set. */ - public static class FromVerbBuilder implements VerbBuilder { - // the atSign that we are authenticating with (e.g. atSignStr.equals("@alice") <=> true) [required] - private String atSignStr; - - public void setAtSign(String atSignStr) { - this.atSignStr = atSignStr; - } - - @Override - public String build() { - atSignStr = AtSign.formatAtSign(atSignStr); - if (atSignStr == null || atSignStr.isEmpty()) { - throw new IllegalArgumentException("atSignStr cannot be null or empty"); - } - return "from:" + atSignStr; - } + @Builder(builderMethodName = "cramCommandBuilder", builderClassName = "CramCommandBuilder") + public static String cram(String digest) { + checkNotNull(digest, "digest not set"); + return "cram:" + digest; } /** - * Atsign Platform cram (Challenge Response Authentication Management) command builder + * A builder to compose an Atsign protocol command with the pol verb. The pol verb + * is part of the pkam process to authenticate oneself while connecting to someone else's atServer. + * The term 'pol' means 'proof of life' as it provides a near realtime assurance that the requestor + * is who it claims to be. + * + * @return A correctly formed pol verb command. */ - public static class CRAMVerbBuilder implements VerbBuilder { - - // chlallenge response authentication method - - private String digest; // the digest to use for authentication, encrypt the challenge (given by the from verb) to get the digest [required] - - public void setDigest(String digest) { - this.digest = digest; - } - - @Override - public String build() { - String s = "cram:" + digest; - return s; - } + @Builder(builderMethodName = "polCommandBuilder", builderClassName = "PolCommandBuilder") + public static String pol() { + return "pol"; } /** - * Atsign Platform pol (Proof of Life) command builder + * A builder to compose an Atsign protocol command with the pkam verb. The pkam verb + * follows the from verb. As an owner of the atServer, you should be able to take the + * challenge thrown by the from verb and encrypt using the private key of the RSA key pair + * with what the server has been bound with. Upon receiving the cram verb along with the digest, the + * server decrypts the digest using the public key and matches it with the challenge. If they are + * the same then the atServer lets you connect to the atServer and changes the prompt to your + * {@link AtSign}. + * + * @param digest The challenge sent as a result of the from verb signed with the {@link AtSign}'s + * private authentication key. + * @param signingAlgo The signing algorithm used. + * @param hashingAlgo The hashing algorithm used. + * @param enrollmentId The specific enrollment id for the {@link AtSign} that matches a specific + * authentication key pair. + * @return A correctly formed pkam verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. */ - public static class POLVerbBuilder implements VerbBuilder { - - @Override - public String build() { - return "pol"; - } + @Builder(builderMethodName = "pkamCommandBuilder", builderClassName = "PkamCommandBuilder") + public static String pkam(String digest, String signingAlgo, String hashingAlgo, EnrollmentId enrollmentId) { + checkNotNull(digest, "digest not set"); + if (enrollmentId != null) { + checkNotBlank(signingAlgo, "signingAlgo not set"); + checkNotBlank(hashingAlgo, "hashingAlgo not set"); + } + + return new StringBuilder("pkam") + .append(signingAlgo != null ? ":signingAlgo:" + signingAlgo : "") + .append(hashingAlgo != null ? ":hashingAlgo:" + hashingAlgo : "") + .append(enrollmentId != null ? ":enrollmentId:" + enrollmentId : "") + .append(':').append(digest) + .toString(); } /** - * Atsign Platform pkam (Public Key Authentication Management) command builder + * A builder to compose an Atsign protocol command with the update verb. The update + * is used to insert key/value pairs into a Key Store. An update command can only be sent by the + * {@link AtSign} that "owns" the key value and can only be sent to their own Atsign server. + * + * @param keyName The namespace qualified key name (without the sharedBy or sharedWith or public, + * hidden or cache qualifiers). + * @param sharedBy The {@link AtSign} which is owns / is sharing this key value (this will qualify + * the keyName is the built command). + * @param sharedWith The {@link AtSign} which is receiving this key value (this will qualify the + * keyName is the built command). + * @param isHidden Denotes whether the key value is hidden (this will qualify the keyName in the + * built command and set the metadata). + * @param isPublic Denotes whether the key value is public (this will qualify the keyName in the + * built command and set the metadata). + * @param isCached Denotes whether the key value is cached (this will qualify the keyName in the + * built command and set the metadata). + * @param ttl Sets the time to live in the metadata (milliseconds). This overrides the metadata + * param if this is also set. + * @param ttb Sets the time to birth in the metadata (milliseconds). This overrides the metadata + * param if this is also set. + * @param ttr Sets the time to refresh (for a cached key) in the metadata (milliseconds). The value + * -1 denotes "cached forever". This overrides the metadata param if this is also set. + * @param ccd Indicates if a cached key needs to be deleted when the atSign user who has originally + * shared it deletes it. This overrides the metadata param if this is also set. + * @param isBinary Sets metadata field which indicates a binary value. This overrides the metadata + * param if this is also set. + * @param isEncrypted Sets metadata field which indicates that value is encrypted. This overrides + * the metadata param if this is also set. + * @param dataSignature sets metadata field that holds signature of the value. This overrides the + * metadata param if this is also set. + * @param sharedKeyEnc Sets metadata field. This overrides the metadata param if this is also set. + * @param pubKeyCS sets metadata field. This overrides the metadata param if this is also set. + * @param encoding sets metadata field. This overrides the metadata param if this is also set. + * @param ivNonce sets metadata field used to hold the encryption initialization vector when value + * is encrypted. This overrides the metadata param if this is also set. + * @param value the value of the key / value. This overrides the metadata param if this is also set. + * @param key a {@link AtKey} instance from which keyName, sharedBy, sharedWith and metadata will be + * taken from. + * @param rawKey the Atsign protocol key with cached and public qualifications + * @return A correctly formed update verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. */ - public static class PKAMVerbBuilder implements VerbBuilder { - - // public key authentication method - - private String digest; // digest the challenge string given by the from verb [required] - - public void setDigest(String digest) { - this.digest = digest; - } - - @Override - public String build() { - String s = "pkam:" + digest; - return s; - } - + @Builder(builderMethodName = "updateCommandBuilder", builderClassName = "UpdateCommandBuilder") + public static String update(String keyName, AtSign sharedBy, AtSign sharedWith, Boolean isHidden, Boolean isPublic, + Boolean isCached, Long ttl, Long ttb, Long ttr, Boolean ccd, Boolean isBinary, + Boolean isEncrypted, String dataSignature, String sharedKeyEnc, String pubKeyCS, + String encoding, String ivNonce, Object value, AtKey key, String rawKey) { + + String metadataString; + String keyString; + + if (key != null) { + checkAllNull("both key and key fields set", keyName, sharedBy, sharedWith, isHidden, isPublic, isCached, ttl, ttb, + ttr, ccd, isBinary, isEncrypted, dataSignature, sharedKeyEnc, pubKeyCS, encoding, ivNonce); + metadataString = key.metadata().toString(); + keyString = key.toString(); + } else { + MetadataBuilder metadataBuilder = createBlankMetadataBuilder(); + if (rawKey == null) { + setIsHiddenIfNotNull(metadataBuilder, isHidden); + setIsPublicIfNotNull(metadataBuilder, isPublic); + setIsCachedIfNotNull(metadataBuilder, isCached); + } else { + checkAllNull("both rawKeys and isHidden, isPublic isCached set", isHidden, isPublic, isCached); + checkAllNull("both rawKeys and key fields set", keyName, sharedBy, sharedWith); + } + setTtlIfNotNull(metadataBuilder, ttl); + setTtrIfNotNull(metadataBuilder, ttr); + setTtbIfNotNull(metadataBuilder, ttb); + setCcdIfNotNull(metadataBuilder, ccd); + setIsBinaryIfNotNull(metadataBuilder, isBinary); + setIsEncryptedIfNotNull(metadataBuilder, isEncrypted); + setDataSignatureIfNotNull(metadataBuilder, dataSignature); + setSharedKeyEncIfNotNull(metadataBuilder, sharedKeyEnc); + setPubKeyCSIfNotNull(metadataBuilder, pubKeyCS); + setEncodingIfNotNull(metadataBuilder, encoding); + setIvNonceIfNotNull(metadataBuilder, ivNonce); + + checkNotBlank(keyName, "keyName not set"); + checkNotNull(sharedBy, "sharedBy not set"); + checkNotNull(value, "value not set"); + + Metadata metadata = metadataBuilder.build(); + metadataString = metadata.toString(); + keyString = rawKey != null ? rawKey : toRawKey(keyName, sharedBy, sharedWith, metadata); + } + + return String.format("update%s:%s %s", metadataString, keyString, value); } /** - * Atsign Platform update command builder + * Controls whether lookups return just the value, just the metadata or the value and metadata */ - public static class UpdateVerbBuilder implements VerbBuilder { - - /// Update the value (and metadata optionally) of a key. - - // ======================================= - // AtKey name details - // ======================================= - private String key; // e.g. "test", "location", "email" [required] - private String sharedBy; // e.g. "@alice" [required] - private String sharedWith = null; // e.g. "@bob" - private Boolean isHidden = null; // if true, adds _ at the beginning of the fullKeyName - private Boolean isPublic = null; // /// if [isPublic] is true, then [atKey] is accessible by all atSigns, if [isPublic] is false, then [atKey] is accessible either by [sharedWith] or [sharedBy] - private Boolean isCached = false; // if true, will add "cached:" to the fullKeyName - private Integer ttl = null; // time to live in milliseconds (how long AtKey will exist) (0 by default) - private Integer ttb = null; // time to birth in milliseconds (how long it will take for AtKey to exist) (0 by default) - private Integer ttr = null; // time to refresh in milliseconds (how long it will take for AtKey to refresh) - private Boolean ccd = null; // if true, cached keys will be deleted if the original key is deleted - private Boolean isBinary = null; // if true, the value contains binary data - private Boolean isEncrypted = null; // if true, the value is encrypted with some encryption key - private String dataSignature = null; // usually public data is signed with the private key to prove that the data is authentic - private String sharedKeyEnc = null; // will be set only when [sharedWith] is set. Will be encrypted using the public key of [sharedWith] atsign - private String pubKeyCS = null; // checksum of the public of of [sharedWith] atSign. Will be set only when [sharedWith] is set. - private String encoding = null; // indicates if public data is encoded. If the public data contains a new line character, the data will be encoded and the encoding will be set to given type of encoding - private String ivNonce = null; - - private Object value; // the value to set [required] - - public void setKeyName(String keyName) { - this.key = keyName; - } - - public void setSharedBy(String sharedBy) { - this.sharedBy = sharedBy; - } - - public void setSharedWith(String sharedWith) { - this.sharedWith = sharedWith; - } - - public void setIsHidden(Boolean isHidden) { - this.isHidden = isHidden; - } - - public void setIsPublic(Boolean isPublic) { - this.isPublic = isPublic; - } - - public void setIsCached(Boolean isCached) { - this.isCached = isCached; - } - - public void setTtl(Integer ttl) { - this.ttl = ttl; - } - - public void setTtb(Integer ttb) { - this.ttb = ttb; - } - - public void setTtr(Integer ttr) { - this.ttr = ttr; - } - - public void setCcd(Boolean ccd) { - this.ccd = ccd; - } - - public void setIsBinary(boolean isBinary) { - this.isBinary = isBinary; - } - - public void setIsEncrypted(boolean isEncrypted) { - this.isEncrypted = isEncrypted; - } - - public void setDataSignature(String dataSignature) { - this.dataSignature = dataSignature; - } - - public void setSharedKeyEnc(String sharedKeyEnc) { - this.sharedKeyEnc = sharedKeyEnc; - } - - public void setPubKeyCS(String pubKeyCS) { - this.pubKeyCS = pubKeyCS; - } - - public void setEncoding(String encoding) { - this.encoding = encoding; - } - - public void setValue(Object value) { - this.value = value; - } - - public void setMetadata(Metadata metadata) { - this.isHidden = metadata.isHidden; - this.isPublic = metadata.isPublic; - this.isCached = metadata.isCached; - this.ttl = metadata.ttl; - this.ttb = metadata.ttb; - this.ttr = metadata.ttr; - this.ccd = metadata.ccd; - this.isBinary = metadata.isBinary; - this.isEncrypted = metadata.isEncrypted; - this.dataSignature = metadata.dataSignature; - this.sharedKeyEnc = metadata.sharedKeyEnc; - this.pubKeyCS = metadata.pubKeyCS; - this.encoding = metadata.encoding; - this.ivNonce = metadata.ivNonce; - } - - public void with(AtKey atKey, Object value) { - setKeyName(atKey.getFullyQualifiedKeyName()); - setSharedBy(atKey.sharedBy.toString()); - if (atKey.sharedWith != null && !atKey.sharedWith.toString().isEmpty()) { - setSharedWith(atKey.sharedWith.toString()); - } - setIsCached(atKey.metadata.isCached); - setIsHidden(atKey.metadata.isHidden); - setIsPublic(atKey.metadata.isPublic); - setMetadata(atKey.metadata); - setValue(value); - } - - @Override - public String build() { - if (key == null || key.isEmpty() || sharedBy == null || sharedBy.isEmpty() || value == null - || value.toString().isEmpty()) { - throw new IllegalArgumentException("keyName, sharedBy, and value cannot be null or empty"); - } - String fullKeyName = buildAtKeyStr(); - String metadata = buildMetadataStr(); - String s = "update" + metadata + ":" + fullKeyName + " " + value.toString(); - return s; - } - - private String buildAtKeyStr() { - String s = ""; - if (isHidden != null && isHidden) { - s += "_"; - } - if (isCached != null && isCached) { - s += "cached:"; - } - if (isPublic != null && isPublic) { - s += "public:"; - } - if (sharedWith != null && !sharedWith.isEmpty()) { - s += AtSign.formatAtSign(sharedWith) + ":"; - } - s += key; - s += AtSign.formatAtSign(sharedBy); - return s; - } - - private String buildMetadataStr() { - Metadata metadata = new Metadata(); - metadata.ttl = ttl; - metadata.ttb = ttb; - metadata.ttr = ttr; - metadata.ccd = ccd; - metadata.isBinary = isBinary; - metadata.isEncrypted = isEncrypted; - metadata.dataSignature = dataSignature; - metadata.sharedKeyEnc = sharedKeyEnc; - metadata.pubKeyCS = pubKeyCS; - metadata.encoding = encoding; - metadata.ivNonce = ivNonce; - return metadata.toString(); - } + public enum LookupOperation { + none, meta, all + }; + /** + * A builder to compose an Atsign protocol command with the llookup verb. The llookup + * verb is used to look up key values "owned" / shared by the {@link AtSign} that is sending the + * command. + * + * @param keyName The namespace qualified key name (without the sharedBy or sharedWith or public, + * hidden or cache qualifiers). + * @param sharedBy The {@link AtSign} which is owns / is sharing this key value (this will qualify + * the keyName is the built command). + * @param sharedWith The {@link AtSign} which is receiving this key value (this will qualify the + * keyName is the built command). + * @param isHidden Denotes whether the key value is hidden (this will qualify the keyName in the + * built command and set the metadata). + * @param isPublic Denotes whether the key value is public (this will qualify the keyName in the + * built command and set the metadata). + * @param isCached Denotes whether the key value is cached (this will qualify the keyName in the + * built command and set the metadata). + * @param operation Controls whether lookups return just the value, just the metadata or the value + * and metadata. + * @param key a {@link AtKey} instance from which keyName, sharedBy, sharedWith and + * public/hidden/cached qualifiers will be taken from. + * @param rawKey The "raw" protocol key string for the key. i.e. includes keyName, sharedBy, + * sharedWith and public/hidden/cached qualifiers (this will override those fields). + * @return A correctly formed llookup verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. + */ + @Builder(builderMethodName = "llookupCommandBuilder", builderClassName = "LlookupCommandBuilder") + public static String llookup(String keyName, AtSign sharedBy, AtSign sharedWith, Boolean isHidden, Boolean isPublic, + Boolean isCached, LookupOperation operation, AtKey key, String rawKey) { + + String operationString = toOperationString(operation); + String keyString; + if (rawKey != null) { + checkAllNull("both rawKey and key fields are set", keyName, sharedBy, sharedWith, key); + keyString = rawKey; + } else if (key != null) { + checkAllNull("both key and key fields are set", keyName, sharedBy, sharedWith, isHidden, isPublic, isCached); + keyString = key.toString(); + } else { + checkNotBlank(keyName, "keyName not set"); + checkNotNull(sharedBy, "sharedBy not set"); + MetadataBuilder metadataBuilder = createBlankMetadataBuilder(); + setIsHiddenIfNotNull(metadataBuilder, isHidden); + setIsPublicIfNotNull(metadataBuilder, isPublic); + setIsCachedIfNotNull(metadataBuilder, isCached); + keyString = toRawKey(keyName, sharedBy, sharedWith, metadataBuilder.build()); + } + return String.format("llookup:%s%s", operationString, keyString); } /** - * Atsign Platform llookup (Local lookup) command builder. - * Used when key is "owned" by the {@link AtSign} sending the command + * A builder to compose an Atsign protocol command with the lookup verb. The lookup + * verb is used to look up key values shared by other {@link AtSign}s with the {@link AtSign} that + * is sending the command. + * + * @param keyName The namespace qualified key name (without the sharedBy or sharedWith or public, + * hidden or cache qualifiers). + * @param sharedBy The {@link AtSign} which is owns / is sharing this key value (this will qualify + * the keyName is the built command). + * @param operation Controls whether lookups return just the value, just the metadata or the value + * and metadata. + * @param key a {@link AtKey} instance from which keyName, sharedBy, sharedWith and + * public/hidden/cached qualifiers will be taken from. + * @param rawKey The "raw" protocol key string for the key. i.e. includes keyName, sharedBy, + * sharedWith and public/hidden/cached qualifiers (this will override those fields). + * @return A correctly formed lookup verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. */ - - public static class LlookupVerbBuilder implements VerbBuilder { - - /** - * Builder argument which controls the scope of the lookup command - */ - public enum Type { - NONE, // llookup: - METADATA, // llookup:meta: - ALL, // llookup:all: - } - - private String key; // e.g. "test", "location", "email" [required] - private String sharedBy; // e.g. sharedBy atSign "@alice" [required] - private String sharedWith = null; // e.g. sharedWith atSign "@bob" - private Boolean isHidden = null; // if true, adds _ at the beginning of the fullKeyName - private Boolean isPublic = null; // if [isPublic] is true, then [atKey] is accessible by all atSigns and "public:" will be added to the fullKeyName, if [isPublic] is false, then [atKey] is accessible either by [sharedWith] or [sharedBy] - private Boolean isCached = null; // if true, will add "cached:" to the fullKeyName - - private Type type = Type.NONE; - - public void setKeyName(String key) { - this.key = key; - } - - public void setSharedBy(String sharedBy) { - this.sharedBy = sharedBy; - } - - public void setSharedWith(String sharedWith) { - this.sharedWith = sharedWith; - } - - public void setIsHidden(Boolean isHidden) { - this.isHidden = isHidden; - } - - public void setIsPublic(Boolean isPublic) { - this.isPublic = isPublic; - } - - public void setIsCached(Boolean isCached) { - this.isCached = isCached; - } - - public void setType(Type type) { - this.type = type; - } - - public void with(AtKey atKey, LlookupVerbBuilder.Type type) { - setKeyName(atKey.getFullyQualifiedKeyName()); - setSharedBy(atKey.sharedBy.toString()); - if (atKey.sharedWith != null && !atKey.sharedWith.toString().isEmpty()) { - setSharedWith(atKey.sharedWith.toString()); - } - setIsHidden(atKey.metadata.isHidden); - setIsPublic(atKey.metadata.isPublic); - setIsCached(atKey.metadata.isCached); - setType(type); - } - - @Override - public String build() { - if (key == null || key.isEmpty() || sharedBy == null || sharedBy.isEmpty()) { - throw new IllegalArgumentException("keyName and sharedBy cannot be null or empty"); - } - String s = "llookup:"; - switch (type) { - case METADATA: - s += "meta:"; - break; - case ALL: - s += "all:"; - break; - default: - break; - } - if (isHidden != null && isHidden) { - s += "_"; - } - if (isCached != null && isCached) { - s += "cached:"; - } - if (isPublic != null && isPublic) { - s += "public:"; - } - if (sharedWith != null && !sharedWith.isEmpty()) { - s += AtSign.formatAtSign(sharedWith) + ":"; - } - s += key; - s += AtSign.formatAtSign(sharedBy); - return s; // eg: "llookup:meta:cached:public:test@bob" - - } - + @Builder(builderMethodName = "lookupCommandBuilder", builderClassName = "LookupCommandBuilder") + public static String lookup(String keyName, AtSign sharedBy, LookupOperation operation, Keys.SharedKey key, + String rawKey) { + String operationString = toOperationString(operation); + String keyString; + if (rawKey != null) { + checkAllNull("both rawKey and key fields are set", keyName, sharedBy, key); + keyString = rawKey; + } else if (key != null) { + checkAllNull("both key and key fields are set", keyName, sharedBy); + keyString = toRawKey(key.name(), key.sharedBy()); + } else { + checkNotBlank(keyName, "keyName not set"); + checkNotNull(sharedBy, "sharedBy not set"); + keyString = toRawKey(keyName, sharedBy); + } + return String.format("lookup:%s%s", operationString, keyString); } /** - * Atsign Platform lookup command builder. - * Used when key shared with the {@link AtSign} sending the command. + * A builder to compose an Atsign protocol command with the plookup verb. The plookup + * verb is used to look up a public key value shared by an {@link AtSign} other than the one that + * is sending the command. + * + * @param keyName The namespace qualified key name (without the sharedBy or sharedWith or public, + * hidden or cache qualifiers). + * @param sharedBy The {@link AtSign} which is owns / is sharing this key value (this will qualify + * the keyName is the built command). + * @param bypassCache If true this forces the value to be fetch from the Atsign server that has + * shared the key value. + * @param operation Controls whether lookups return just the value, just the metadata or the value + * and metadata. + * @param key a {@link AtKey} instance from which keyName, sharedBy, sharedWith and + * public/hidden/cached qualifiers will be taken from. + * @param rawKey The "raw" protocol key string for the key. i.e. includes keyName, sharedBy, + * sharedWith and public/hidden/cached qualifiers (this will override those fields). + * @return A correctly formed plookup verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. */ - public static class LookupVerbBuilder implements VerbBuilder { - - /** - * Builder argument which controls the scope of the lookup command - */ - public enum Type { - NONE, // lookup: - METADATA, // lookup:meta: - ALL, // lookup:all: - } - - private String key; // key name e.g. "test", "location", "email" [required] - private String sharedBy; // sharedBy atSign e.g. "@alice" [required] (not your atSign, the atSign of another secondary, get) - - private Type type = Type.NONE; - - public void setKeyName(String key) { - this.key = key; - } - - public void setSharedBy(String sharedBy) { - this.sharedBy = sharedBy; - } - - public void setType(Type type) { - this.type = type; - } - - public void with(SharedKey sharedKey, LookupVerbBuilder.Type type) { - setKeyName(sharedKey.getFullyQualifiedKeyName()); - setSharedBy(sharedKey.sharedBy.toString()); - setType(type); - } - - @Override - public String build() { - if (key == null || key.isEmpty() || sharedBy == null || sharedBy.isEmpty()) { - throw new IllegalArgumentException("keyName and sharedBy cannot be null or empty"); - } - String s = "lookup:"; - switch (type) { - case METADATA: - s += "meta:"; - break; - case ALL: - s += "all:"; - break; - default: - break; - } - s += this.key; - s += AtSign.formatAtSign(this.sharedBy); - return s; // eg: "lookup:meta:test@bob" - } + @Builder(builderMethodName = "plookupCommandBuilder", builderClassName = "PlookupCommandBuilder") + public static String plookup(String keyName, AtSign sharedBy, Boolean bypassCache, LookupOperation operation, + AtKey key, String rawKey) { + + String bypassCacheString = toBypassCacheString(bypassCache); + String operationString = toOperationString(operation); + String keyString; + if (rawKey != null) { + checkAllNull("both rawKey and key fields are set", keyName, sharedBy, key); + keyString = rawKey; + } else if (key != null) { + checkAllNull("both key and key fields are set", keyName, sharedBy); + keyString = toRawKey(key.name(), key.sharedBy()); + } else { + checkNotBlank(keyName, "keyName not set"); + checkNotNull(sharedBy, "sharedBy not set"); + keyString = toRawKey(keyName, sharedBy); + } + + return String.format("plookup:%s%s%s", bypassCacheString, operationString, keyString); } /** - * Atsign Platform plookup (public lookup) command builder. - * Used when key is a public key owned by an {@link AtSign} other than the one sending the command. + * A builder to compose an Atsign protocol command with the delete verb. The delete + * verb is used to remove key/value pairs into a Key Store. An delete command can only be + * sent by the {@link AtSign} that "owns" the key value and can only be sent to their own Atsign + * server. + * + * @param keyName The namespace qualified key name (without the sharedBy or sharedWith or public, + * hidden or cache qualifiers). + * @param sharedBy The {@link AtSign} which is owns / is sharing this key value (this will qualify + * the keyName is the built command). + * @param sharedWith The {@link AtSign} which is receiving this key value (this will qualify the + * keyName is the built command). + * @param isHidden Denotes whether the key value is hidden (this will qualify the keyName in the + * built command and set the metadata). + * @param isPublic Denotes whether the key value is public (this will qualify the keyName in the + * built command and set the metadata). + * @param isCached Denotes whether the key value is cached (this will qualify the keyName in the + * built command and set the metadata). + * @param key a {@link AtKey} instance from which keyName, sharedBy, sharedWith and metadata will be + * taken from. + * @return A correctly formed delete verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. */ - public static class PlookupVerbBuilder implements VerbBuilder { - - /** - * Builder argument which controls the scope of the lookup command - */ - public enum Type { - NONE, // just get the data - METADATA, // get the metadata but no data (plookup:meta:) - ALL, // get the data and metadata (plookup:all:) - } - - // AtKey details - private String key; // key name (e.g. location, test) [required] - private String sharedBy; // sharedBy atSign ("@bob") [required] - - private Boolean bypassCache = false; // bypass cache (plookup:bypassCache:[true/false]) [optional] - - private Type type = Type.NONE; - - public void setKeyName(String key) { - this.key = key; - } - - public void setSharedBy(String sharedBy) { - this.sharedBy = sharedBy; - } - - public void setType(Type type) { - this.type = type; - } - - public void setBypassCache(Boolean bypassCache) { - this.bypassCache = bypassCache; - } - - public void with(PublicKey atKey, PlookupVerbBuilder.Type type) { - setKeyName(atKey.getFullyQualifiedKeyName()); - setSharedBy(atKey.sharedBy.toString()); - setType(type); - } - - @Override - public String build() { - if (this.key == null || this.key.isEmpty() || this.sharedBy == null || this.sharedBy.isEmpty()) { - throw new IllegalArgumentException("key or sharedBy is null or empty"); - } - String s = "plookup:"; - if (this.bypassCache != null && this.bypassCache) { - s += "bypassCache:true:"; - } - switch (type) { - case METADATA: - s += "meta:"; - break; - case ALL: - s += "all:"; - break; - default: - break; - } - s += this.key; - s += AtSign.formatAtSign(this.sharedBy); - - return s; // e.g: "plookup:meta:@alice:test@bob" - } - + @Builder(builderMethodName = "deleteCommandBuilder", builderClassName = "DeleteCommandBuilder") + public static String delete(String keyName, AtSign sharedBy, AtSign sharedWith, Boolean isHidden, Boolean isPublic, + Boolean isCached, AtKey key, String rawKey) { + + String keyString; + if (rawKey != null) { + checkAllNull("both rawKey and key fields are set", keyName, sharedBy, sharedWith, key); + keyString = rawKey; + } else if (key != null) { + checkAllNull("both key and isHidden, isPublic, isCached are set", + keyName, sharedBy, sharedWith, isHidden, isPublic, isCached); + keyString = key.toString(); + } else { + MetadataBuilder metadataBuilder = createBlankMetadataBuilder(); + setIsHiddenIfNotNull(metadataBuilder, isHidden); + setIsPublicIfNotNull(metadataBuilder, isPublic); + setIsCachedIfNotNull(metadataBuilder, isCached); + checkNotBlank(keyName, "keyName not set"); + checkNotNull(sharedBy, "sharedBy not set"); + keyString = toRawKey(keyName, sharedBy, sharedWith, metadataBuilder.build()); + } + + return String.format("delete:%s", keyString); } /** - * Atsign Platform delete command builder. - * Used to delete a key owned by the {@link AtSign} sending the command. + * A builder to compose an Atsign protocol command with the scan verb. The scan verb + * is used to list the keys in an {@link AtSign}'s Atsign server. + * + * @param regex If set only show keys that match this regular expression pattern. + * @param fromAtSign If set only show keys that are created by the {@link AtSign}. + * @param showHidden If true, will show hidden internal keys. + * @return A correctly formed scan verb command. */ - public static class DeleteVerbBuilder implements VerbBuilder { - - private String key; // e.g. "test", "location", "email" [required] - private String sharedBy; // e.g. sharedBy atSign "@alice" [required] - private String sharedWith = ""; // e.g. sharedWith atSign "@bob" - private Boolean isHidden = false; - private Boolean isPublic = false; // if [isPublic] is true, then [atKey] is accessible by all atSigns and "public:" will be added to the fullKeyName, if [isPublic] is false, then [atKey] is accessible either by [sharedWith] or [sharedBy] - private Boolean isCached = false; // if true, will add "cached:" to the fullKeyName + @Builder(builderMethodName = "scanCommandBuilder", builderClassName = "ScanCommandBuilder") + public static String scan(String regex, AtSign fromAtSign, Boolean showHidden) { - public void setKeyName(String keyName) { - this.key = keyName; + StringBuilder builder = new StringBuilder("scan"); + if (isTrue(showHidden)) { + builder.append(":showHidden:true"); } - - public void setSharedBy(String sharedBy) { - this.sharedBy = sharedBy; - } - - public void setSharedWith(String sharedWith) { - this.sharedWith = sharedWith; + if (fromAtSign != null) { + builder.append(':').append(fromAtSign); } - - public void setIsHidden(Boolean isHidden) { - this.isHidden = isHidden; + if (!isBlank(regex)) { + builder.append(' ').append(regex); } + return builder.toString(); + } - public void setIsPublic(Boolean isPublic) { - this.isPublic = isPublic; - } + /** + * A builder to compose an Atsign protocol command with the notify:messageType:text verb. + * The notify:messageType:text verb is used to send an arbitrary message to another + * {@link AtSign}. + * + * @param recipient The {@link AtSign} you wish to send the message to. + * @param text The message. + * @return A correctly formed notify:messageType:text verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. + */ + @Builder(builderMethodName = "notifyTextCommandBuilder", builderClassName = "NotifyTextCommandBuilder") + public static String notifyText(AtSign recipient, String text) { + checkNotNull(recipient, "recipient not set"); + checkNotBlank(text, "text not set"); + return String.format("notify:messageType:text:%s:%s", recipient, text); + } - public void setIsCached(Boolean isCached) { - this.isCached = isCached; - } + /** + * The type of key change operation + */ + public enum NotifyOperation { + update, delete + } - public void with(AtKey atKey) { - setKeyName(atKey.getFullyQualifiedKeyName()); - setSharedBy(atKey.sharedBy.toString()); - if (atKey.sharedWith != null && !atKey.sharedWith.toString().isEmpty()) { - setSharedWith(atKey.sharedWith.toString()); - } - setIsHidden(atKey.metadata.isHidden); - setIsPublic(atKey.metadata.isPublic); - setIsCached(atKey.metadata.isCached); - } + /** + * A builder to compose an Atsign protocol command with the + * notify:(update|delete):messageType:key verb. + * The notify:(update|delete):messageType:key verb is used to send key change notifications + * to other + * {@link AtSign}s. + * + * @param operation Update or Delete. + * @param recipient The {@link AtSign} to send the notification to. + * @param sender The {@link AtSign} that is sending the notification. + * @param key The namespace qualified key name. + * @param value The updated value for the key. + * @param ttr Sets the time to refresh (milliseconds). + * @return A correctly formed notify:messageType:key verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. + */ + @Builder(builderMethodName = "notifyKeyChangeCommandBuilder", builderClassName = "NotifyKeyChangeCommandBuilder") + public static String notifyKeyChange(NotifyOperation operation, AtSign recipient, AtSign sender, String key, + String value, + Long ttr) { + + checkNotBlank(key, "key not set"); + checkNotNull(operation, "operation not set"); + + if (ttr != null) { + checkTrue(ttr >= -1, "ttr < -1"); + checkNotBlank(value, "value not set (mandatory when ttr is set)"); + } + + return new StringBuilder("notify:") + .append(operation) + .append(":messageType:key:") + .append(ttr != null ? "ttr:" + ttr + ":" : "") + .append(recipient != null ? recipient + ":" : "") + .append(key) + .append(sender != null ? sender : "") + .append(!isBlank(value) ? ":" + value : "") + .toString(); + } - @Override - public String build() { - if (key == null || sharedBy == null) { - throw new IllegalArgumentException("key or sharedBy is null. These are required fields"); - } + /** + * A builder to compose an Atsign protocol command with the notify:status verb. + * The notify:status verb is query the status of a previously sent notification. + * + * @param notificationId The unique id of a notification. This will have been the response for to a + * previously sent notify:(update:delete) command. + * @return A correctly formed notify:status verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. + */ + @Builder(builderMethodName = "notifyStatusCommandBuilder", builderClassName = "NotifyStatusCommandBuilder") + public static String notifyStatus(String notificationId) { - String s = "delete:"; - if (isHidden) { - s += "_"; - } - if (isCached) { - s += "cached:"; - } - if (isPublic) { - s += "public:"; - } - if (sharedWith != null && !sharedWith.isEmpty()) { - s += AtSign.formatAtSign(sharedWith) + ":"; - } - s += key; - s += AtSign.formatAtSign(sharedBy); - return s; // eg: "delete:cached:public:test@bob" - } + checkNotBlank(notificationId, "notificationId not set"); + return "notify:status:" + notificationId; } /** - * Atsign Platform scan command builder. - * Used to list all the keys visible to the {@link AtSign} sending the command. + * Types of enroll operations */ - public static class ScanVerbBuilder implements VerbBuilder { - - // Regex to filter the keys - private String regex; + public enum EnrollOperation { + request, approve, deny, revoke, list, fetch, unrevoke, delete + } - // Scans the keys shared by forAtSign - private String fromAtSign; + /** + * JSON member names used in enroll parameters + */ + public static class EnrollParameters { + public static final String ENROLLMENT_ID = "enrollmentId"; + public static final String ENCRYPTED_PRIVATE_KEY = "encryptedDefaultEncryptionPrivateKey"; + public static final String PRIVATE_KEY_IV = "encPrivateKeyIV"; + public static final String ENCRYPTED_SELF_ENCRYPTION_KEY = "encryptedDefaultSelfEncryptionKey"; + public static final String SELF_ENCRYPTION_KEY_IV = "selfEncKeyIV"; + public static final String ENROLLMENT_STATUS_FILTER = "enrollmentStatusFilter"; + public static final String APP_NAME = "appName"; + public static final String DEVICE_NAME = "deviceName"; + public static final String APKAM_PUBLIC_KEY = "apkamPublicKey"; + public static final String ENCRYPTED_APKAM_SYMMETRIC_KEY = "encryptedAPKAMSymmetricKey"; + public static final String OTP = "otp"; + public static final String NAMESPACES = "namespaces"; + public static final String APKAM_KEYS_EXPIRY_IN_MILLIS = "apkamKeysExpiryInMillis"; + } - // Scans for hidden keys (showHidden:true) - private boolean showHidden = false; + /** + * A builder to compose an Atsign protocol command with the enroll verb. + * The enroll verb is used to submit an APKAM enrollment. + * + * @param operation The specific enroll operation to perform see {@link EnrollOperation}. + * @param status Filters {@link EnrollOperation#list} operations. + * @param enrollmentId The unique enrollment id to refer to in fetch, approve,deny,delete,revoke or + * unrevoke. + * @param encryptPrivateKey The private encryption key which all enrollments share for an + * {@link AtSign}. This is mandatory for an approve and the value should be encrypted with + * the APKAM symmetric key that was provided by the {@link EnrollOperation#request}. + * @param encryptPrivateKeyIv The initialization vector used for the private key encryption. + * @param selfEncryptKey The self encryption key which all enrollments share for an + * {@link AtSign}. This is mandatory for an approve and the value should be encrypted with + * the APKAM symmetric key that was provided by the {@link EnrollOperation#request}. + * @param selfEncryptKeyIv The initialization vector used for the self key encryption. + * @param appName The application name qualifier for the appName deviceName combination we are + * enrolling. + * @param deviceName The device name qualifier for the appName deviceName combination we are + * enrolling. + * @param apkamPublicKey The public authentication key for the appName and device we are enrolling. + * @param otp The one time password for {@link EnrollOperation#request}. + * @param namespaces A map of namespace access control associations. e.g. ns1 {@code -->} rw, ns2 + * {@code -->} r (r = read only, rw = read write). + * @param apkamSymmetricKey A one-time symmetric key used for the duration of the enrollment + * workflow. This should be encrypted with the public encryption key of the {@link AtSign}. + * @param ttl The time to live for the enrollment request. + * @return A correctly formed enroll verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. + */ + @Builder(builderMethodName = "enrollCommandBuilder", builderClassName = "EnrollCommandBuilder") + public static String enroll(EnrollOperation operation, String status, EnrollmentId enrollmentId, + String encryptPrivateKey, String encryptPrivateKeyIv, String selfEncryptKey, + String selfEncryptKeyIv, String appName, String deviceName, String apkamPublicKey, + String otp, Map namespaces, String apkamSymmetricKey, + long ttl) { - public void setRegex(String regex) { - this.regex = regex; - } + checkNotNull(operation, "operation not set"); - public void setFromAtSign(String fromAtSign) { - this.fromAtSign = fromAtSign; - } + Object params = null; - public void setShowHidden(boolean showHidden) { - this.showHidden = showHidden; + switch (operation) { + case list: + if (!isBlank(status)) { + params = singletonMap(EnrollParameters.ENROLLMENT_STATUS_FILTER, singletonList(status)); + } + break; + case approve: + checkNotNull(enrollmentId, "enrollmentId not set"); + checkNotNull(encryptPrivateKey, "encryptPrivateKey not set"); + checkNotNull(encryptPrivateKeyIv, "encryptPrivateKeyIv not set"); + checkNotNull(selfEncryptKey, "selfEncryptKey not set"); + checkNotNull(selfEncryptKeyIv, "selfEncryptKeyIv not set"); + params = toObjectMap(EnrollParameters.ENROLLMENT_ID, enrollmentId, + EnrollParameters.ENCRYPTED_PRIVATE_KEY, encryptPrivateKey, + EnrollParameters.PRIVATE_KEY_IV, encryptPrivateKeyIv, + EnrollParameters.ENCRYPTED_SELF_ENCRYPTION_KEY, selfEncryptKey, + EnrollParameters.SELF_ENCRYPTION_KEY_IV, selfEncryptKeyIv); + break; + case fetch: + case deny: + case revoke: + case unrevoke: + case delete: + checkNotNull(enrollmentId, "enrollmentId not set"); + params = toObjectMap(EnrollParameters.ENROLLMENT_ID, enrollmentId); + break; + case request: + checkNotBlank(appName, "appName not set"); + checkNotBlank(deviceName, "deviceName not set"); + checkNotBlank(apkamPublicKey, "apkamPublicKey not set"); + if (otp == null) { + params = toObjectMap(EnrollParameters.APP_NAME, appName, + EnrollParameters.DEVICE_NAME, deviceName, + EnrollParameters.APKAM_PUBLIC_KEY, apkamPublicKey); + } else { + checkNotBlank(otp, "otp not set"); + checkNotNull(namespaces, "namespaces not set"); + params = toObjectMap(EnrollParameters.APP_NAME, appName, + EnrollParameters.DEVICE_NAME, deviceName, + EnrollParameters.APKAM_PUBLIC_KEY, apkamPublicKey, + EnrollParameters.ENCRYPTED_APKAM_SYMMETRIC_KEY, apkamSymmetricKey, + EnrollParameters.OTP, otp, + EnrollParameters.NAMESPACES, namespaces, + EnrollParameters.APKAM_KEYS_EXPIRY_IN_MILLIS, ttl); + } + break; + default: + throw new IllegalArgumentException("unsupported operation"); } - // r'^scan$|scan(:(?@[^:@\s]+))?(:page:(?\d+))?( (?\S+))?$'; - public String build() { - - String command = "scan"; - - if (showHidden) { - command += ":showHidden:true"; - } - - if (fromAtSign != null && !isBlank(fromAtSign)) { - command += ":" + fromAtSign; - } - - if (regex != null && !isBlank(regex)) { - command += " " + regex; - } - - return command; - } + return new StringBuilder("enroll:") + .append(operation) + .append(params != null ? encodeAsJson(params) : "") + .toString(); } /** - * Atsign Platform notify command builder. + * Types of operation for keys verb */ - public static class NotifyTextVerbBuilder implements VerbBuilders.VerbBuilder { - //notify:((?update|delete):)?(messageType:(?key|text):)?(priority:(?low|medium|high):)?(strategy:(?all|latest):)?(latestN:(?\d+):)?(notifier:(?[^\s:]+):)?(ttln:(?\d+):)?(ttl:(?\d+):)?(ttb:(?\d+):)?(ttr:(?(-)?\d+):)?(ccd:(?true|false):)?(@(?[^@:\s]*)):(?[^:@]((?!:{2})[^@])+)(@(?[^@:\s]+))?(:(?.+))?$ - - private String recipientAtSign; - private String text; - + public enum KeysOperation { + put, get, delete + }; - public void setRecipientAtSign(String recipientAtSign) { - this.recipientAtSign = recipientAtSign; + /** + * A builder to compose an Atsign protocol command with the keys verb. + * The keys verb is specifically used to update security keys in the Atsign server. + * + * @param operation put, get or delete. + * @param keyName The full qualified key name which includes sharedBy, sharedWith, namespace and + * visibility qualifiers. + * @return A correctly formed keys verb command. + * @throws IllegalArgumentException If mandatory fields are not set or if field values conflict. + */ + @Builder(builderMethodName = "keysCommandBuilder", builderClassName = "KeysCommandBuilder") + public static String keys(KeysOperation operation, String keyName) { + checkNotNull(operation, "operation not set"); + if (operation == KeysOperation.get) { + checkNotBlank(keyName, "keyName not set"); + return String.format("keys:get:keyName:%s", keyName); + } else { + throw new IllegalArgumentException(operation + " not supported"); } + } + /** + * A builder to compose an Atsign protocol command with the otp verb. + * The otp verb is used to request a one time password for the enrollment workflow. + * + * @return A correctly formed otp verb command. + */ + @Builder(builderMethodName = "otpCommandBuilder", builderClassName = "OtpCommandBuilder") + public static String otp() { + return "otp:get"; + } - public void setText(String text) { - this.text = text; + protected static Map toObjectMap(Object... nameValuePairs) { + if ((nameValuePairs.length % 2) != 0) { + throw new IllegalArgumentException("odd number of parameters"); } - - - public String build() { - - if (recipientAtSign == null || recipientAtSign.isEmpty()) { - throw new IllegalArgumentException("recipientAtSign cannot be null or empty"); - } - - if (text == null || text.isEmpty()) { - throw new IllegalArgumentException("text cannot be null or empty"); - } - - if (!recipientAtSign.startsWith("@")) { - recipientAtSign = "@" + recipientAtSign; + Map map = new LinkedHashMap<>(); + for (int i = 0; i < nameValuePairs.length; i++) { + String key = nameValuePairs[i].toString(); + Object value = nameValuePairs[++i]; + if (value instanceof TypedString) { + map.put(key, value.toString()); + } else { + map.put(key, value); } - - return "notify:messageType:text:" + recipientAtSign + ":" + text; } + return map; } - /** - * Atsign Platform notify command builder. - */ - public static class NotifyKeyChangeBuilder implements VerbBuilders.VerbBuilder { - - // Only allowed values are "update" or "delete" - private String operation = "update"; - // Optional if key contains the recipient - private String recipientAtSign; - private String key; - // Optional if key contains the owner - private String senderAtSign; - // Value to be notified when the change needs to be cached. - private String value; - - // If the key has to be cached by the other @sign - // ttr of -1 indicated cache forever without a need to refresh the value again. - // Any other positive value indicates time after which the value needs to be refreshed - private final int defaultTTRValue = -2; - private long ttr = defaultTTRValue; - - - public void setOperation(String operation) { - this.operation = operation; - } - - // This is optional if the key is fully formed. i.e. key in the format @recipientAtSign:phone@senderAtSign - public void setRecipientAtSign(String recipientAtSign) { - this.recipientAtSign = recipientAtSign; + protected static String encodeAsJson(Object o) { + try { + return Json.MAPPER.writeValueAsString(o); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("json encoding exception", e); } + } + private static boolean isTrue(Boolean bool) { + return bool != null && bool; + } - public void setKey(String key) { - this.key = key; - } + private static String toRawKey(String keyName, AtSign sharedBy) { + return toRawKey(keyName, sharedBy, null, EMPTY_METADATA); + } - // This is optional if the key is fully formed. i.e. key in the format @recipientAtSign:phone@senderAtSign - public void setSenderAtSign(String senderAtSign) { - this.senderAtSign = senderAtSign; + private static String toRawKey(String keyName, AtSign sharedBy, AtSign sharedWith, Metadata metadata) { + StringBuilder builder = new StringBuilder(); + if (isTrue(metadata.isHidden())) { + builder.append("_"); } - - - public void setValue(String value) { - this.value = value; + if (isTrue(metadata.isCached())) { + builder.append("cached:"); } - - - public void setTtr(long ttr) { - this.ttr = ttr; + if (isTrue(metadata.isPublic())) { + builder.append("public:"); } - - - //notify:((?update|delete):)?(messageType:(?key|text):)?(priority:(?low|medium|high):)?(strategy:(?all|latest):)?(latestN:(?\d+):)?(notifier:(?[^\s:]+):)?(ttln:(?\d+):)?(ttl:(?\d+):)?(ttb:(?\d+):)?(ttr:(?(-)?\d+):)?(ccd:(?true|false):)?(@(?[^@:\s]*)):(?[^:@]((?!:{2})[^@])+)(@(?[^@:\s]+))?(:(?.+))?$ - public String build() { - - if (key == null || isBlank(key)) { - throw new IllegalArgumentException("key cannot be null or empty"); - } - - - if (!"update".equals(operation) && !"delete".equals(operation)) { - throw new IllegalArgumentException("Only 'update' and 'delete' are allowed for operation"); - } - - if (ttr < -1 && ttr != defaultTTRValue) { - throw new IllegalArgumentException("Invalid value for ttr. Only -1 and positive numbers are allowed"); - } - - if (ttr != defaultTTRValue && (value == null || isBlank(value))) { - throw new IllegalArgumentException("When the ttr is specified value cannot be null or empty"); - } - - String command = "notify:" + operation + ":messageType:key:"; - - // append ttr - if (ttr != defaultTTRValue) { - command += "ttr:" + ttr + ":"; - } - - // append recipients @sign if it is not part of the key already - if (recipientAtSign != null && !isBlank(recipientAtSign)) { - - if (!recipientAtSign.startsWith("@")) { - recipientAtSign = "@" + recipientAtSign; - } - - command += recipientAtSign + ":"; - } - - // append the key - command += key; - - if (senderAtSign != null && !isBlank(senderAtSign)) { - - if (!senderAtSign.startsWith("@")) { - senderAtSign = "@" + senderAtSign; - } - - command += senderAtSign; - } - - if (value != null && !isBlank(value)) { - command += ":" + value; - } - - return command; + if (sharedWith != null) { + builder.append(sharedWith).append(':'); } + builder.append(keyName); + builder.append(sharedBy); + return builder.toString(); } - /** - * Atsign Platform notify command builder. - */ - public static class NotificationStatusVerbBuilder implements VerbBuilders.VerbBuilder { - - private String notificationId; - - public void setNotificationId(String notificationId) { - this.notificationId = notificationId; + private static String toOperationString(LookupOperation operation) { + if (operation == null || operation == LookupOperation.none) { + return ""; } + return operation + ":"; + } - //notify:status:(?\S+)$'; - public String build() { - - if (notificationId == null || isBlank(notificationId)) { - throw new IllegalArgumentException("notificationId cannot be null or empty"); - } - - return "notify:status:" + notificationId; - } + private static String toBypassCacheString(Boolean bypassCache) { + return isTrue(bypassCache) ? "bypassCache:true:" : ""; } + private static MetadataBuilder createBlankMetadataBuilder() { + return Metadata.builder() + .isPublic(null) + .isEncrypted(null) + .isHidden(null) + .namespaceAware(null) + .isBinary(null) + .isCached(null); + } } diff --git a/at_client/src/test/java/org/atsign/client/api/AtKeyNamesTest.java b/at_client/src/test/java/org/atsign/client/api/AtKeyNamesTest.java new file mode 100644 index 00000000..3ff710d8 --- /dev/null +++ b/at_client/src/test/java/org/atsign/client/api/AtKeyNamesTest.java @@ -0,0 +1,17 @@ +package org.atsign.client.api; + +import org.junit.jupiter.api.Test; + +import static org.atsign.client.api.AtKeyNames.*; +import static org.atsign.common.AtSign.createAtSign; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +class AtKeyNamesTest { + + @Test + void testToSharedByMeKeyNameReturnsExpectedValue() { + assertThat(toSharedByMeKeyName(createAtSign("gary")), equalTo("shared_key.gary")); + } + +} diff --git a/at_client/src/test/java/org/atsign/client/api/AtKeysTest.java b/at_client/src/test/java/org/atsign/client/api/AtKeysTest.java index b5adcb37..d79af312 100644 --- a/at_client/src/test/java/org/atsign/client/api/AtKeysTest.java +++ b/at_client/src/test/java/org/atsign/client/api/AtKeysTest.java @@ -1,372 +1,100 @@ package org.atsign.client.api; import static org.atsign.client.util.EnrollmentId.createEnrollmentId; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.jupiter.api.Assertions.*; +import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.util.Base64; import java.util.Map; -import org.atsign.client.util.EnrollmentId; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class AtKeysTest { - private AtKeys atKeys; - private static final String TEST_ENROLLMENT_ID = "test-enrollment-123"; - private static final String TEST_SELF_ENCRYPT_KEY = "testSelfEncryptKey123"; - private static final String TEST_APKAM_SYMMETRIC_KEY = "testApkamSymmetricKey"; - - @BeforeEach - void setUp() { - atKeys = new AtKeys(); - } - - // EnrollmentId Tests - @Test void testHasEnrollmentIdReturnsFalseWhenNotSet() { - assertFalse(atKeys.hasEnrollmentId()); + AtKeys keys = AtKeys.builder().build(); + assertFalse(keys.hasEnrollmentId()); } @Test void testHasEnrollmentIdReturnsTrueWhenSet() { - atKeys.setEnrollmentId(createEnrollmentId(TEST_ENROLLMENT_ID)); - assertTrue(atKeys.hasEnrollmentId()); - } - - @Test - void testSetAndGetEnrollmentId() { - EnrollmentId enrollmentId = createEnrollmentId(TEST_ENROLLMENT_ID); - atKeys.setEnrollmentId(enrollmentId); - assertEquals(enrollmentId, atKeys.getEnrollmentId()); - } - - @Test - void testSetEnrollmentIdSupportsChaining() { - EnrollmentId enrollmentId = createEnrollmentId(TEST_ENROLLMENT_ID); - AtKeys result = atKeys.setEnrollmentId(enrollmentId); - assertSame(atKeys, result); - } - - @Test - void testGetEnrollmentIdReturnsNullWhenNotSet() { - assertNull(atKeys.getEnrollmentId()); - } - - // Self Encryption Key Tests - - @Test - void testSetAndGetSelfEncryptionKey() { - atKeys.setSelfEncryptKey(TEST_SELF_ENCRYPT_KEY); - assertEquals(TEST_SELF_ENCRYPT_KEY, atKeys.getSelfEncryptKey()); - } - - @Test - void testSetSelfEncryptKeySupportsChaining() { - AtKeys result = atKeys.setSelfEncryptKey(TEST_SELF_ENCRYPT_KEY); - assertSame(atKeys, result); - } - - @Test - void testGetSelfEncryptionKeyReturnsNullWhenNotSet() { - assertNull(atKeys.getSelfEncryptKey()); - } - - // APKAM Public Key Tests - - @Test - void testSetAndGetApkamPublicKeyAsString() { - String testKey = "testPublicKey123"; - atKeys.setApkamPublicKey(testKey); - assertEquals(testKey, atKeys.getApkamPublicKey()); - } - - @Test - void testSetApkamPublicKeyFromPublicKeyObject() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - KeyPair keyPair = keyGen.generateKeyPair(); - - atKeys.setApkamPublicKey(keyPair.getPublic()); - - assertNotNull(atKeys.getApkamPublicKey()); - String expected = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); - assertEquals(expected, atKeys.getApkamPublicKey()); - } - - @Test - void testSetApkamPublicKeyWithStringSupportsChaining() { - AtKeys result = atKeys.setApkamPublicKey("testKey"); - assertSame(atKeys, result); + AtKeys keys = AtKeys.builder().enrollmentId(createEnrollmentId("123")).build(); + assertTrue(keys.hasEnrollmentId()); } @Test - void testSetApkamPublicKeyWithObjectSupportsChaining() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - KeyPair keyPair = keyGen.generateKeyPair(); - AtKeys result = atKeys.setApkamPublicKey(keyPair.getPublic()); - assertSame(atKeys, result); - } - - // APKAM Private Key Tests - - @Test - void testSetAndGetApkamPrivateKeyAsString() { - String testKey = "testPrivateKey123"; - atKeys.setApkamPrivateKey(testKey); - assertEquals(testKey, atKeys.getApkamPrivateKey()); - } - - @Test - void testSetApkamPrivateKeyFromPrivateKeyObject() throws Exception { + void testApkamKeyPairBuilderExtension() throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); KeyPair keyPair = keyGen.generateKeyPair(); - atKeys.setApkamPrivateKey(keyPair.getPrivate()); + AtKeys keys = AtKeys.builder() + .apkamKeyPair(keyPair) + .build(); - assertNotNull(atKeys.getApkamPrivateKey()); - String expected = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); - assertEquals(expected, atKeys.getApkamPrivateKey()); + assertThat(keys.getApkamPublicKey(), equalTo(toBase64(keyPair.getPublic()))); + assertThat(keys.getApkamPrivateKey(), equalTo(toBase64(keyPair.getPrivate()))); } @Test - void testSetApkamPrivateKeyWithStringSupportsChaining() { - AtKeys result = atKeys.setApkamPrivateKey("testKey"); - assertSame(atKeys, result); - } - - @Test - void testSetApkamPrivateKeyWithObjectSupportsChaining() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - KeyPair keyPair = keyGen.generateKeyPair(); - AtKeys result = atKeys.setApkamPrivateKey(keyPair.getPrivate()); - assertSame(atKeys, result); - } - - // APKAM Key Pair Tests - - @Test - void testSetApkamKeyPair() throws Exception { + void testEncryptKeyPairBuilderExtension() throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); KeyPair keyPair = keyGen.generateKeyPair(); - atKeys.setApkamKeyPair(keyPair); - - assertNotNull(atKeys.getApkamPublicKey()); - assertNotNull(atKeys.getApkamPrivateKey()); - - String expectedPublic = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); - String expectedPrivate = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); - - assertEquals(expectedPublic, atKeys.getApkamPublicKey()); - assertEquals(expectedPrivate, atKeys.getApkamPrivateKey()); - } - - @Test - void testSetApkamKeyPairSupportsChaining() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - KeyPair keyPair = keyGen.generateKeyPair(); - AtKeys result = atKeys.setApkamKeyPair(keyPair); - assertSame(atKeys, result); - } - - // hasPkamKeys Tests + AtKeys keys = AtKeys.builder() + .encryptKeyPair(keyPair) + .build(); - @Test - void testHasPkamKeysReturnsFalseWhenNotSet() { - assertFalse(atKeys.hasPkamKeys()); + assertThat(keys.getEncryptPublicKey(), equalTo(toBase64(keyPair.getPublic()))); + assertThat(keys.getEncryptPrivateKey(), equalTo(toBase64(keyPair.getPrivate()))); } @Test - void testHasPkamKeysReturnsFalseWhenOnlyPublicKeySet() { - atKeys.setApkamPublicKey("testPublicKey"); - assertFalse(atKeys.hasPkamKeys()); + void testHasPkamKeyReturnsFalseWhenNotSet() { + AtKeys keys = AtKeys.builder().build(); + assertFalse(keys.hasPkamKey()); } @Test - void testHasPkamKeysReturnsFalseWhenOnlyPrivateKeySet() { - atKeys.setApkamPrivateKey("testPrivateKey"); - assertFalse(atKeys.hasPkamKeys()); + void testHasPkamKeyReturnsFalseWhenOnlyPublicKeySet() { + AtKeys keys = AtKeys.builder().apkamPublicKey("xxxx").build(); + assertFalse(keys.hasPkamKey()); } @Test - void testHasPkamKeysReturnsTrueWhenBothSet() { - atKeys.setApkamPublicKey("testPublicKey"); - atKeys.setApkamPrivateKey("testPrivateKey"); - assertTrue(atKeys.hasPkamKeys()); + void testHasPkamKeyReturnsTrueWhenPrivateKeySet() { + AtKeys keys = AtKeys.builder().apkamPrivateKey("xxxx").build(); + assertTrue(keys.hasPkamKey()); } - // Encrypt Public Key Tests - @Test - void testSetAndGetEncryptPublicKeyAsString() { - String testKey = "testEncryptPublicKey"; - atKeys.setEncryptPublicKey(testKey); - assertEquals(testKey, atKeys.getEncryptPublicKey()); - } - - @Test - void testSetEncryptPublicKeyFromPublicKeyObject() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - KeyPair keyPair = keyGen.generateKeyPair(); - - atKeys.setEncryptPublicKey(keyPair.getPublic()); - - assertNotNull(atKeys.getEncryptPublicKey()); - String expected = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); - assertEquals(expected, atKeys.getEncryptPublicKey()); + void testGetReturnsNullForKeyBeforePut() { + AtKeys keys = AtKeys.builder().build(); + assertThat(keys.get("key1"), nullValue()); } @Test - void testSetEncryptPublicKeyWithStringSupportsChaining() { - AtKeys result = atKeys.setEncryptPublicKey("testKey"); - assertSame(atKeys, result); - } - - @Test - void testSetEncryptPublicKeyWithObjectSupportsChaining() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - KeyPair keyPair = keyGen.generateKeyPair(); - AtKeys result = atKeys.setEncryptPublicKey(keyPair.getPublic()); - assertSame(atKeys, result); - } - - // Encrypt Private Key Tests - - @Test - void testSetAndGetEncryptPrivateKeyAsString() { - String testKey = "testEncryptPrivateKey"; - atKeys.setEncryptPrivateKey(testKey); - assertEquals(testKey, atKeys.getEncryptPrivateKey()); - } - - @Test - void testSetEncryptPrivateKeyFromPrivateKeyObject() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - KeyPair keyPair = keyGen.generateKeyPair(); - - atKeys.setEncryptPrivateKey(keyPair.getPrivate()); - - assertNotNull(atKeys.getEncryptPrivateKey()); - String expected = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); - assertEquals(expected, atKeys.getEncryptPrivateKey()); - } - - @Test - void testSetEncryptPrivateKeyWithStringSupportsChaining() { - AtKeys result = atKeys.setEncryptPrivateKey("testKey"); - assertSame(atKeys, result); - } - - @Test - void testSetEncryptPrivateKeyWithObjectSupportsChaining() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - KeyPair keyPair = keyGen.generateKeyPair(); - AtKeys result = atKeys.setEncryptPrivateKey(keyPair.getPrivate()); - assertSame(atKeys, result); - } - - // Encrypt Key Pair Tests - - @Test - void testSetEncryptKeyPair() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - KeyPair keyPair = keyGen.generateKeyPair(); - - atKeys.setEncryptKeyPair(keyPair); - - assertNotNull(atKeys.getEncryptPublicKey()); - assertNotNull(atKeys.getEncryptPrivateKey()); - - String expectedPublic = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); - String expectedPrivate = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); - - assertEquals(expectedPublic, atKeys.getEncryptPublicKey()); - assertEquals(expectedPrivate, atKeys.getEncryptPrivateKey()); - } - - @Test - void testSetEncryptKeyPairSupportsChaining() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - KeyPair keyPair = keyGen.generateKeyPair(); - AtKeys result = atKeys.setEncryptKeyPair(keyPair); - assertSame(atKeys, result); - } - - // APKAM Symmetric Key Tests - - @Test - void testSetAndGetApkamSymmetricKey() { - atKeys.setApkamSymmetricKey(TEST_APKAM_SYMMETRIC_KEY); - assertEquals(TEST_APKAM_SYMMETRIC_KEY, atKeys.getApkamSymmetricKey()); - } - - @Test - void testSetApkamSymmetricKeySupportsChaining() { - AtKeys result = atKeys.setApkamSymmetricKey(TEST_APKAM_SYMMETRIC_KEY); - assertSame(atKeys, result); - } - - @Test - void testGetApkamSymmetricKeyReturnsNullWhenNotSet() { - assertNull(atKeys.getApkamSymmetricKey()); - } - - // Cache Tests - - @Test - void testPutAndGetFromCache() { - String key = "customKey"; - String value = "customValue"; - - atKeys.put(key, value); - assertEquals(value, atKeys.get(key)); - } - - @Test - void testGetReturnsNullForNonExistentKey() { - assertNull(atKeys.get("nonExistentKey")); - } - - @Test - void testPutOverwritesExistingKeyValue() { - String key = "testKey"; - atKeys.put(key, "value1"); - atKeys.put(key, "value2"); - assertEquals("value2", atKeys.get(key)); - } - - @Test - void testPutMultipleKeys() { - atKeys.put("key1", "value1"); - atKeys.put("key2", "value2"); - atKeys.put("key3", "value3"); - - assertEquals("value1", atKeys.get("key1")); - assertEquals("value2", atKeys.get("key2")); - assertEquals("value3", atKeys.get("key3")); + void testGetReturnsExpectedValueForKeyAfterPut() { + AtKeys keys = AtKeys.builder().build(); + keys.put("key1", "value1"); + assertThat(keys.get("key1"), equalTo("value1")); + assertThat(keys.get("key2"), nullValue()); } @Test void testGetCacheReturnsUnmodifiableMap() { - atKeys.put("key1", "value1"); - atKeys.put("key2", "value2"); + AtKeys keys = AtKeys.builder().build(); + keys.put("key1", "value1"); + keys.put("key2", "value2"); - Map cache = atKeys.getCache(); + Map cache = keys.getCache(); assertNotNull(cache); assertEquals(2, cache.size()); @@ -378,26 +106,8 @@ void testGetCacheReturnsUnmodifiableMap() { }); } - @Test - void testGetCacheReturnsEmptyUnmodifiableMapWhenEmpty() { - Map cache = atKeys.getCache(); - - assertNotNull(cache); - assertTrue(cache.isEmpty()); - - assertThrows(UnsupportedOperationException.class, () -> { - cache.put("key1", "value1"); - }); + private static String toBase64(Key key) { + return Base64.getEncoder().encodeToString(key.getEncoded()); } - @Test - void testGetCacheReflectsChanges() { - atKeys.put("key1", "value1"); - Map cache1 = atKeys.getCache(); - assertEquals(1, cache1.size()); - - atKeys.put("key2", "value2"); - Map cache2 = atKeys.getCache(); - assertEquals(2, cache2.size()); - } } diff --git a/at_client/src/test/java/org/atsign/common/AtClientValidationIT.java b/at_client/src/test/java/org/atsign/common/AtClientValidationIT.java index 66fdd8b7..fdd270fd 100644 --- a/at_client/src/test/java/org/atsign/common/AtClientValidationIT.java +++ b/at_client/src/test/java/org/atsign/common/AtClientValidationIT.java @@ -4,9 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import org.atsign.client.util.AtClientValidation; -import org.atsign.common.Keys.PublicKey; -import org.atsign.common.Keys.SelfKey; -import org.atsign.common.Keys.SharedKey; +import org.atsign.common.exceptions.AtSecondaryConnectException; +import org.atsign.common.exceptions.AtSecondaryNotFoundException; import org.atsign.cucumber.helpers.Helpers; import org.atsign.virtualenv.VirtualEnv; import org.junit.jupiter.api.BeforeAll; @@ -34,120 +33,22 @@ public void atSignExistsTest() { }); // empty atSign - assertThrows(IllegalArgumentException.class, () -> { + assertThrows(AtSecondaryNotFoundException.class, () -> { AtSign atSign = new AtSign(""); AtClientValidation.atSignExists(atSign, VE_ROOT_URL); }); // root does not contain atSign - assertThrows(AtException.class, () -> { + assertThrows(AtSecondaryNotFoundException.class, () -> { AtSign atSign = new AtSign("someAtSignThatDNE456"); AtClientValidation.atSignExists(atSign, VE_ROOT_URL); }); // invalid root - assertThrows(AtException.class, () -> { + assertThrows(AtSecondaryConnectException.class, () -> { AtSign atSign = new AtSign("smoothalligator"); AtClientValidation.atSignExists(atSign, "invalidroot:32123"); }); } - - // validate AtKey object is ready (checks atKey.name validity, metadata - // validity, and if sharedWith exists) - @Test - public void validateAtKeyTest() { - - // ==================================== - // PublicKey tests - // ==================================== - - // null publicKey - assertThrows(AtException.class, () -> { - PublicKey publicKey = null; - AtClientValidation.validateAtKey(publicKey, VE_ROOT_URL); - }); - - // public key with invalid ttr - assertThrows(AtException.class, () -> { - AtSign sharedBy = new AtSign("@bob"); - PublicKey publicKey = new KeyBuilders.PublicKeyBuilder(sharedBy).key("test").build(); - publicKey.metadata.ttr = -2; - AtClientValidation.validateAtKey(publicKey, ""); - }); - - // ==================================== - // SelfKey tests - // ==================================== - - // null selfKey - assertThrows(AtException.class, () -> { - SelfKey selfKey = null; - AtClientValidation.validateAtKey(selfKey, VE_ROOT_URL); - }); - - // self key with invalid keyName - assertThrows(AtException.class, () -> { - AtSign sharedBy = new AtSign("@bob"); - SelfKey selfKey = new KeyBuilders.SelfKeyBuilder(sharedBy).key("t est").build(); - AtClientValidation.validateAtKey(selfKey, VE_ROOT_URL); - }); - - // self key with non-existent sharedWith atSign in root - assertThrows(AtException.class, () -> { - AtSign sharedBy = new AtSign("@nonexistentatsign"); - AtSign sharedWith = new AtSign("@nonexistentatsign"); // atSign does not exist in root - SelfKey selfKey = new KeyBuilders.SelfKeyBuilder(sharedBy, sharedWith).key("test").build(); - AtClientValidation.validateAtKey(selfKey, VE_ROOT_URL); - }); - - // ==================================== - // SharedKey tests - // ==================================== - - // null shared key test - assertThrows(AtException.class, () -> { - SharedKey sharedKey = null; - AtClientValidation.validateAtKey(sharedKey, VE_ROOT_URL); - }); - - // shared key with ttr < -1 - assertThrows(AtException.class, () -> { - AtSign sharedBy = new AtSign("@bob"); - AtSign sharedWith = new AtSign("@alice"); - SharedKey sharedKey = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key("test").build(); - sharedKey.metadata.ttr = -22323; - AtClientValidation.validateAtKey(sharedKey, VE_ROOT_URL); - }); - - // shared key with invalid keyName - assertThrows(AtException.class, () -> { - AtSign sharedBy = new AtSign("@bob"); - AtSign sharedWith = new AtSign("@alice"); - SharedKey sharedKey = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key("t est").build(); - AtClientValidation.validateAtKey(sharedKey, VE_ROOT_URL); - }); - - // shared key with invalid sharedWith atSign (atSign dne in root) - assertThrows(AtException.class, () -> { - AtSign sharedBy = new AtSign("@wildgreen"); - AtSign sharedWith = new AtSign("@nonexistentatsign"); // atSign does not exist in root - SharedKey sharedKey = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key("test").build(); - AtClientValidation.validateAtKey(sharedKey, VE_ROOT_URL); - }); - - // empty root url - assertThrows(AtException.class, () -> { - AtSign sharedBy = new AtSign("@bob"); - AtSign sharedWith = new AtSign("@alice"); - SharedKey sharedKey = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key("test").build(); - AtClientValidation.validateAtKey(sharedKey, ""); - }); - - // ==================================== - // PrivateHiddenKey tests - // ==================================== - - // TODO - } } diff --git a/at_client/src/test/java/org/atsign/common/AtClientValidationTest.java b/at_client/src/test/java/org/atsign/common/AtClientValidationTest.java deleted file mode 100644 index 50989dfd..00000000 --- a/at_client/src/test/java/org/atsign/common/AtClientValidationTest.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.atsign.common; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import org.atsign.client.util.AtClientValidation; -import org.atsign.common.exceptions.AtInvalidAtKeyException; -import org.junit.jupiter.api.Test; - -public class AtClientValidationTest { - - // valid key names (like "test", but not "cached:public:test@bob" <-- these key - // names will always fail this test) - @Test - public void validateKeyNameTest() { - // null key name - AtException ex = assertThrows(AtInvalidAtKeyException.class, () -> { - String keyName = null; - AtClientValidation.validateKeyName(keyName); - }); - assertThat(ex.getMessage(), equalTo("Key cannot be null or empty")); - - // empty key name - ex = assertThrows(AtInvalidAtKeyException.class, () -> { - String keyName = ""; - AtClientValidation.validateKeyName(keyName); - }); - assertThat(ex.getMessage(), equalTo("Key cannot be null or empty")); - - // key name with @ - ex = assertThrows(AtInvalidAtKeyException.class, () -> { - String keyName = "test@bob"; - AtClientValidation.validateKeyName(keyName); - }); - assertThat(ex.getMessage(), equalTo("Key cannot contain @")); - - // key name with spaces - ex = assertThrows(AtInvalidAtKeyException.class, () -> { - String keyName = " te st "; - AtClientValidation.validateKeyName(keyName); - }); - assertThat(ex.getMessage(), equalTo("Key cannot have spaces")); - } - - // valid metadata (null check, ttl, ttb, ttr) - @Test - public void validateMetadataTest() { - - // null metadata - AtException ex = assertThrows(AtInvalidAtKeyException.class, () -> { - Metadata metadata = null; - AtClientValidation.validateMetadata(metadata); - }); - assertThat(ex.getMessage(), equalTo("Metadata cannot be null")); - - // null ttl - ex = assertThrows(AtInvalidAtKeyException.class, () -> { - Metadata metadata = new Metadata(); - metadata.ttl = null; - metadata.ttb = 0; - metadata.ttr = -1; - AtClientValidation.validateMetadata(metadata); - }); - assertThat(ex.getMessage(), equalTo("ttl cannot be null and cannot be negative")); - - // negative ttl - ex = assertThrows(AtInvalidAtKeyException.class, () -> { - Metadata metadata = new Metadata(); - metadata.ttl = -100; - metadata.ttb = 0; - metadata.ttr = -1; - AtClientValidation.validateMetadata(metadata); - }); - assertThat(ex.getMessage(), equalTo("ttl cannot be null and cannot be negative")); - - // null ttb - ex = assertThrows(AtInvalidAtKeyException.class, () -> { - Metadata metadata = new Metadata(); - metadata.ttl = 0; - metadata.ttb = null; - metadata.ttr = -1; - AtClientValidation.validateMetadata(metadata); - }); - assertThat(ex.getMessage(), equalTo("ttb cannot be null and cannot be negative")); - - // negative ttb - ex = assertThrows(AtInvalidAtKeyException.class, () -> { - Metadata metadata = new Metadata(); - metadata.ttl = 0; - metadata.ttb = -100; - metadata.ttr = -1; - AtClientValidation.validateMetadata(metadata); - }); - assertThat(ex.getMessage(), equalTo("ttb cannot be null and cannot be negative")); - - // null ttr - ex = assertThrows(AtInvalidAtKeyException.class, () -> { - Metadata metadata = new Metadata(); - metadata.ttl = 0; - metadata.ttb = 0; - metadata.ttr = null; - AtClientValidation.validateMetadata(metadata); - }); - assertThat(ex.getMessage(), equalTo("ttr cannot be null and cannot be < -1")); - - // ttr < -1 - ex = assertThrows(AtInvalidAtKeyException.class, () -> { - Metadata metadata = new Metadata(); - metadata.ttl = 0; - metadata.ttb = 0; - metadata.ttr = -2; - AtClientValidation.validateMetadata(metadata); - }); - assertThat(ex.getMessage(), equalTo("ttr cannot be null and cannot be < -1")); - - } -} diff --git a/at_client/src/test/java/org/atsign/common/AtSignTest.java b/at_client/src/test/java/org/atsign/common/AtSignTest.java new file mode 100644 index 00000000..4fc0edab --- /dev/null +++ b/at_client/src/test/java/org/atsign/common/AtSignTest.java @@ -0,0 +1,31 @@ +package org.atsign.common; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +import org.junit.jupiter.api.Test; + +class AtSignTest { + + @Test + void testStaticCreateMethod() { + assertThat(AtSign.createAtSign("@fred"), equalTo(new AtSign("@fred"))); + assertThat(AtSign.createAtSign("fred"), equalTo(new AtSign("@fred"))); + assertThat(AtSign.createAtSign(null), nullValue()); + assertThat(AtSign.createAtSign(""), nullValue()); + } + + @Test + void testWithoutPrefixReturnsExpectedResult() { + assertThat(new AtSign("fred").withoutPrefix(), equalTo("fred")); + assertThat(new AtSign("@fred").withoutPrefix(), equalTo("fred")); + } + + @Test + void testFormatAtSignReturnsExpectedResults() { + assertThat(AtSign.formatAtSign("@fred"), equalTo("@fred")); + assertThat(AtSign.formatAtSign("fred"), equalTo("@fred")); + assertThat(AtSign.formatAtSign("@fred "), equalTo("@fred")); + } +} diff --git a/at_client/src/test/java/org/atsign/common/FromStringTest.java b/at_client/src/test/java/org/atsign/common/FromStringTest.java index 9e3b0bf1..e2dc639e 100644 --- a/at_client/src/test/java/org/atsign/common/FromStringTest.java +++ b/at_client/src/test/java/org/atsign/common/FromStringTest.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; +import static org.atsign.common.AtSign.createAtSign; import static org.junit.jupiter.api.Assertions.*; public class FromStringTest { @@ -14,13 +15,14 @@ public class FromStringTest { @Test public void fromStringTest0() throws AtException { // no llookup meta - AtKey atKey = Keys.fromString("public:publickey@alice"); - - assertEquals(true, atKey.metadata.isPublic); - assertEquals(false, atKey.metadata.isEncrypted); - assertEquals(false, atKey.metadata.isHidden); - assertEquals("publickey", atKey.name); - assertEquals("@alice", atKey.sharedBy.toString()); + AtKey atKey = Keys.keyBuilder().rawKey("public:publickey@alice").build(); + + Metadata metadata = atKey.metadata(); + assertEquals(true, metadata.isPublic()); + assertEquals(false, metadata.isEncrypted()); + assertEquals(false, metadata.isHidden()); + assertEquals("publickey", atKey.name()); + assertEquals(createAtSign("@alice"), atKey.sharedBy()); } @Test @@ -35,26 +37,27 @@ public void fromStringTest1() throws AtException, JsonProcessingException { + "\"isBinary\":false,\"isEncrypted\":false," + "\"dataSignature\":null,\"sharedKeyEnc\":null,\"pubKeyCS\":null}"); - AtKey atKey = Keys.fromString(KEY_NAME_STR, response); + AtKey atKey = fromString(KEY_NAME_STR, response); assertEquals(KEY_NAME_STR, atKey.toString()); - assertEquals(true, atKey.metadata.isPublic); - assertEquals("publickey", atKey.name); - assertEquals("@bob", atKey.sharedBy.toString()); - assertEquals("2022-07-13T21:54:28.519Z", atKey.metadata.createdAt.toString()); - assertEquals("2022-07-13T21:54:28.519Z", atKey.metadata.updatedAt.toString()); - assertEquals("2022-07-13T21:54:28.519Z", atKey.metadata.availableAt.toString()); - assertNull(atKey.metadata.expiresAt); - assertNull(atKey.metadata.refreshAt); - assertEquals(0, atKey.metadata.ttl.intValue()); - assertEquals(0, atKey.metadata.ttb.intValue()); - assertNull(atKey.metadata.ttr); - assertNull(atKey.metadata.ccd); - assertEquals(false, atKey.metadata.isBinary); - assertEquals(false, atKey.metadata.isEncrypted); - assertNull(atKey.metadata.dataSignature); - assertNull(atKey.metadata.sharedKeyEnc); - assertNull(atKey.metadata.pubKeyCS); + Metadata metadata = atKey.metadata(); + assertEquals(true, metadata.isPublic()); + assertEquals("publickey", atKey.name()); + assertEquals("@bob", atKey.sharedBy().toString()); + assertEquals("2022-07-13T21:54:28.519Z", metadata.createdAt().toString()); + assertEquals("2022-07-13T21:54:28.519Z", metadata.updatedAt().toString()); + assertEquals("2022-07-13T21:54:28.519Z", metadata.availableAt().toString()); + assertNull(metadata.expiresAt()); + assertNull(metadata.refreshAt()); + assertEquals(0, metadata.ttl().intValue()); + assertEquals(0, metadata.ttb().intValue()); + assertNull(metadata.ttr()); + assertNull(metadata.ccd()); + assertEquals(false, metadata.isBinary()); + assertEquals(false, metadata.isEncrypted()); + assertNull(metadata.dataSignature()); + assertNull(metadata.sharedKeyEnc()); + assertNull(metadata.pubKeyCS()); } @Test @@ -72,29 +75,38 @@ public void fromStringTest2() throws AtException, JsonProcessingException { Secondary.Response response = new Secondary.Response(); response.setRawDataResponse(LLOOKUP_META_STR); - AtKey atKey = Keys.fromString(KEY_NAME_STR, response); + AtKey atKey = fromString(KEY_NAME_STR, response); assertEquals(KEY_NAME_STR, atKey.toString()); - assertEquals(false, atKey.metadata.isPublic); - assertEquals("test", atKey.name); - assertEquals("@bob", atKey.sharedBy.toString()); - assertEquals("2022-07-27T22:12:58.077Z", atKey.metadata.createdAt.toString()); - assertEquals("2022-07-27T22:12:58.077Z", atKey.metadata.updatedAt.toString()); - assertEquals("2022-07-27T22:12:58.077Z", atKey.metadata.availableAt.toString()); - assertEquals("2022-07-27T22:42:58.077Z", atKey.metadata.expiresAt.toString()); - assertNull(atKey.metadata.refreshAt); - assertEquals(1800000, atKey.metadata.ttl.intValue()); - assertEquals(0, atKey.metadata.ttb.intValue()); - assertNull(atKey.metadata.ttr); - assertNull(atKey.metadata.ccd); - assertFalse(atKey.metadata.isBinary); - assertTrue(atKey.metadata.isEncrypted); + Metadata metadata = atKey.metadata(); + assertEquals(false, metadata.isPublic()); + assertEquals("test", atKey.name()); + assertEquals("@bob", atKey.sharedBy().toString()); + assertEquals("2022-07-27T22:12:58.077Z", metadata.createdAt().toString()); + assertEquals("2022-07-27T22:12:58.077Z", metadata.updatedAt().toString()); + assertEquals("2022-07-27T22:12:58.077Z", metadata.availableAt().toString()); + assertEquals("2022-07-27T22:42:58.077Z", metadata.expiresAt().toString()); + assertNull(metadata.refreshAt()); + assertEquals(1800000L, metadata.ttl()); + assertEquals(0L, metadata.ttb()); + assertNull(metadata.ttr()); + assertNull(metadata.ccd()); + assertFalse(metadata.isBinary()); + assertTrue(metadata.isEncrypted()); // noinspection SpellCheckingInspection assertEquals( "oIq0kHvwQieVrhOs4dJLN61qNP73bNLLNPTRW7tAdapIZF3kSMrNVCcTAWWWyzb2Tyii51uZ7zlIYmHWuS4tIE0lMzrUeXGcfQhOrdjkrxf4qEceNR1qLa7tDjOAb8xuhf/zJ3yaen8NGswfKWwQluga/52SchFClrR99xEI93s=", - atKey.metadata.dataSignature); - assertNull(atKey.metadata.sharedKeyEnc); - assertNull(atKey.metadata.pubKeyCS); + metadata.dataSignature()); + assertNull(metadata.sharedKeyEnc()); + assertNull(metadata.pubKeyCS()); } + public static AtKey fromString(String rawKey, Secondary.Response metadataResponse) + throws AtException, JsonProcessingException { + Metadata metadata = Metadata.fromJson(metadataResponse.getRawDataResponse()); + return Keys.keyBuilder() + .rawKey(rawKey) + .metadata(metadata) + .build(); + } } diff --git a/at_client/src/test/java/org/atsign/common/JsonTest.java b/at_client/src/test/java/org/atsign/common/JsonTest.java new file mode 100644 index 00000000..1f350c22 --- /dev/null +++ b/at_client/src/test/java/org/atsign/common/JsonTest.java @@ -0,0 +1,7 @@ +package org.atsign.common; + +import static org.junit.jupiter.api.Assertions.*; + +class JsonTest { + +} diff --git a/at_client/src/test/java/org/atsign/common/KeyStringUtilTest.java b/at_client/src/test/java/org/atsign/common/KeyStringUtilTest.java deleted file mode 100644 index 8de5799d..00000000 --- a/at_client/src/test/java/org/atsign/common/KeyStringUtilTest.java +++ /dev/null @@ -1,510 +0,0 @@ -package org.atsign.common; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.jupiter.api.Assertions.*; - -import org.atsign.client.util.KeyStringUtil; -import org.atsign.client.util.KeyStringUtil.KeyType; -import org.junit.jupiter.api.Test; - -public class KeyStringUtilTest { - // https://docs.google.com/spreadsheets/d/1EOcF_vznBoKWXxRT8dbeG47RP7wnF30ubwXghganTlk/edit#gid=0 - - // Row 4 Public key - @Test - public void publicKey() { - String KEY_NAME = "public:phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("public:phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("phone", keyStringUtil.getKeyName()); - assertNull(null, keyStringUtil.getNamespace()); - assertEquals(KeyType.PUBLIC_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - // Row 5 Public Hidden key - @Test - public void publicKey2() { - String KEY_NAME = "public:_phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("public:_phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("_phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.PUBLIC_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertTrue(keyStringUtil.isHidden()); - } - - // Row 6 Public Hidden key - @Test - public void publicKey3() { - String KEY_NAME = "public:__phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("public:__phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("__phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.PUBLIC_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertTrue(keyStringUtil.isHidden()); - } - - // Row 7A Public key and SharedWith populated - public void publicKey4A() { - String KEY_NAME = "public:@bob:phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("public:@bob:phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.PUBLIC_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertEquals("@bob", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - // Row 7B Public key and SharedWith populated - @Test - public void publicKey4B() { - String KEY_NAME = "public:@alice:phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("public:@alice:phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.PUBLIC_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertEquals("@alice", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - } - - // Row 8 self key (sharedWith not populated) - @Test - public void selfKey1() { - String KEY_NAME = "phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SELF_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - // Row 9 Self key (sharedWith populated) - @Test - public void selfKey2() { - String KEY_NAME = "@bob:phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@bob:phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SELF_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertEquals("@bob", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - // Row 10 Self hidden key (sharedWith populated) - @Test - public void selfKey3() { - String KEY_NAME = "@bob:_phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@bob:_phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("_phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SELF_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertEquals("@bob", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertTrue(keyStringUtil.isHidden()); - } - - // row 11 Self Hidden Key without sharedWith - @Test - public void selfKey4() { - String KEY_NAME = "_phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("_phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("_phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.PRIVATE_HIDDEN_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertTrue(keyStringUtil.isHidden()); - } - - // Row 12 SharedKey - @Test - public void sharedKey1() { - String KEY_NAME = "@bob:phone@alice"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@bob:phone@alice", keyStringUtil.getFullKeyName()); - assertEquals("phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SHARED_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedWith()); - assertEquals("@alice", keyStringUtil.getSharedBy()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - // Row 13 Shared and hidden - @Test - public void sharedKey2() { - String KEY_NAME = "@alice:_phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@alice:_phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("_phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SHARED_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertEquals("@alice", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertTrue(keyStringUtil.isHidden()); - } - - // Row 14A Private keys - @Test - public void privateKey1() { - String KEY_NAME = "private:phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("private:phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.PRIVATE_HIDDEN_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertTrue(keyStringUtil.isHidden()); - } - - // Row 14B Private keys - @Test - public void privateKey2() { - String KEY_NAME = "privatekey:phone@bob"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("privatekey:phone@bob", keyStringUtil.getFullKeyName()); - assertEquals("phone", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.PRIVATE_HIDDEN_KEY, keyStringUtil.getKeyType()); - assertEquals("@bob", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertTrue(keyStringUtil.isHidden()); - } - - /** - * 0: @farinataanxious:lemon@sportsunconscious - * 1: @farinataanxious:shared_key@sportsunconscious - * 2: @farinataanxious:test@sportsunconscious - * 3: @sportsunconscious:shared_key@sportsunconscious - * 4: @sportsunconscious:signing_privatekey@sportsunconscious - * 5: public:publickey@farinataanxious - * 6: public:publickey@sportsunconscious - * 7: public:signing_publickey@sportsunconscious - * 8: shared_key.farinataanxious@sportsunconscious - * 9: shared_key.sportsunconscious@sportsunconscious - */ - @Test - public void suKey1() { - String KEY_NAME = "@farinataanxious:lemon@sportsunconscious"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@farinataanxious:lemon@sportsunconscious", keyStringUtil.getFullKeyName()); - assertEquals("lemon", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SHARED_KEY, keyStringUtil.getKeyType()); - assertEquals("@sportsunconscious", keyStringUtil.getSharedBy()); - assertEquals("@farinataanxious", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void suKey2() { - String KEY_NAME = "@farinataanxious:shared_key@sportsunconscious"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@farinataanxious:shared_key@sportsunconscious", keyStringUtil.getFullKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals("shared_key", keyStringUtil.getKeyName()); - assertEquals(KeyType.SHARED_KEY, keyStringUtil.getKeyType()); - assertEquals("@sportsunconscious", keyStringUtil.getSharedBy()); - assertEquals("@farinataanxious", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void suKey3() { - String KEY_NAME = "@farinataanxious:test@sportsunconscious"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@farinataanxious:test@sportsunconscious", keyStringUtil.getFullKeyName()); - assertEquals("test", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SHARED_KEY, keyStringUtil.getKeyType()); - assertEquals("@sportsunconscious", keyStringUtil.getSharedBy()); - assertEquals("@farinataanxious", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void suKey4() { - String KEY_NAME = "@sportsunconscious:shared_key@sportsunconscious"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@sportsunconscious:shared_key@sportsunconscious", keyStringUtil.getFullKeyName()); - assertEquals("shared_key", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SELF_KEY, keyStringUtil.getKeyType()); - assertEquals("@sportsunconscious", keyStringUtil.getSharedBy()); - assertEquals("@sportsunconscious", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void suKey5() { - String KEY_NAME = "@sportsunconscious:signing_privatekey@sportsunconscious"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@sportsunconscious:signing_privatekey@sportsunconscious", keyStringUtil.getFullKeyName()); - assertEquals("signing_privatekey", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SELF_KEY, keyStringUtil.getKeyType()); - assertEquals("@sportsunconscious", keyStringUtil.getSharedBy()); - assertEquals("@sportsunconscious", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void suKey6() { - String KEY_NAME = "public:publickey@farinataanxious"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("public:publickey@farinataanxious", keyStringUtil.getFullKeyName()); - assertEquals("publickey", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.PUBLIC_KEY, keyStringUtil.getKeyType()); - assertEquals("@farinataanxious", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void suKey7() { - String KEY_NAME = "public:publickey@sportsunconscious"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("public:publickey@sportsunconscious", keyStringUtil.getFullKeyName()); - assertEquals("publickey", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.PUBLIC_KEY, keyStringUtil.getKeyType()); - assertEquals("@sportsunconscious", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void suKey8() { - String KEY_NAME = "shared_key.farinataanxious@sportsunconscious"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("shared_key.farinataanxious@sportsunconscious", keyStringUtil.getFullKeyName()); - assertEquals("shared_key.farinataanxious", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SELF_KEY, keyStringUtil.getKeyType()); - assertEquals("@sportsunconscious", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void suKey9() { - String KEY_NAME = - "atconnections.hacktheleague.smoothalligator.at_contact.mospherepro.hacktheleague@smoothalligator"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("atconnections.hacktheleague.smoothalligator.at_contact.mospherepro.hacktheleague@smoothalligator", - keyStringUtil.getFullKeyName()); - assertEquals("atconnections.hacktheleague.smoothalligator.at_contact.mospherepro", keyStringUtil.getKeyName()); - assertEquals("hacktheleague", keyStringUtil.getNamespace()); - - } - - /** - * smoothalligator - * 0: @abbcservicesinc:shared_key@smoothalligator - * 1: @denise:shared_key@smoothalligator - * 2: @er_nobile_14:shared_key@smoothalligator - * 3: @fascinatingsnow:shared_key@smoothalligator - * 4: @hacktheleague:shared_key@smoothalligator - * 5: @smoothalligator:signing_privatekey@smoothalligator - * 6: @wildgreen:shared_key@smoothalligator - * 7: - * atconnections.abbcservicesinc.smoothalligator.at_contact.mospherepro.abbcservicesinc@smoothalligator - * 8: atconnections.denise.smoothalligator.at_contact.mospherepro.denise@smoothalligator - * 9: - * atconnections.hacktheleague.smoothalligator.at_contact.mospherepro.hacktheleague@smoothalligator - * 10: atconnections.wildgreen.smoothalligator.at_contact.mospherepro.wildgreen@smoothalligator - * 11: @smoothalligator:shared_key@abbcservicesinc - * 12: @smoothalligator:shared_key@denise - * 13: @smoothalligator:shared_key@fascinatingsnow - * 14: @smoothalligator:shared_key@wildgreen - * 15: public:firstname.wavi.wavi@abbcservicesinc - * 16: public:firstname.wavi.wavi@wildgreen - * 17: public:image.wavi.wavi@abbcservicesinc - * 18: public:image.wavi.wavi@denise - * 19: public:image.wavi.wavi@wildgreen - * 20: public:lastname.wavi.wavi@abbcservicesinc - * 21: public:lastname.wavi.wavi@wildgreen - * 22: public:publickey@abbcservicesinc - * 23: public:publickey@denise - * 24: public:publickey@er_nobile_14 - * 25: public:publickey@fascinatingsnow - * 26: public:publickey@hacktheleague - * 27: public:publickey@wildgreen - * 28: public:email.wavi.wavi@smoothalligator - * 29: public:field_order_of_self.wavi.wavi@smoothalligator - * 30: public:firstname.wavi.wavi@smoothalligator - * 31: public:following_by_self.at_follows.wavi.at_follows@smoothalligator - * 32: public:lastname.wavi.wavi@smoothalligator - * 33: public:privateaccount.wavi.wavi@smoothalligator - * 34: public:publickey@smoothalligator - * 35: public:signing_publickey@smoothalligator - * 36: public:theme_color.wavi.wavi@smoothalligator - * 37: publickey.fascinatingsnow.fascinatingsnow@smoothalligator - * 38: senthistory_v2.mospherepro.mospherepro@smoothalligator - * 39: shared_key.abbcservicesinc@smoothalligator - * 40: shared_key.denise@smoothalligator - * 41: shared_key.er_nobile_14@smoothalligator - * 42: shared_key.fascinatingsnow@smoothalligator - * 43: shared_key.hacktheleague@smoothalligator - * 44: shared_key.wildgreen@smoothalligator - */ - - @Test - public void saTest1() { - String KEY_NAME = "@abbcservicesinc:shared_key@smoothalligator"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@abbcservicesinc:shared_key@smoothalligator", keyStringUtil.getFullKeyName()); - assertEquals("shared_key", keyStringUtil.getKeyName()); - assertEquals(KeyType.SHARED_KEY, keyStringUtil.getKeyType()); - assertEquals("@smoothalligator", keyStringUtil.getSharedBy()); - assertEquals("@abbcservicesinc", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void saTest2() { - String KEY_NAME = "@denise:shared_key@smoothalligator"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@denise:shared_key@smoothalligator", keyStringUtil.getFullKeyName()); - assertEquals("shared_key", keyStringUtil.getKeyName()); - assertEquals(KeyType.SHARED_KEY, keyStringUtil.getKeyType()); - assertEquals("@smoothalligator", keyStringUtil.getSharedBy()); - assertEquals("@denise", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void saTest3() { - String KEY_NAME = "@er_nobile_14:shared_key@smoothalligator"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@er_nobile_14:shared_key@smoothalligator", keyStringUtil.getFullKeyName()); - assertEquals("shared_key", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SHARED_KEY, keyStringUtil.getKeyType()); - assertEquals("@smoothalligator", keyStringUtil.getSharedBy()); - assertEquals("@er_nobile_14", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - assertNull(keyStringUtil.getNamespace()); - } - - @Test - public void saTest4() { - String KEY_NAME = "@fascinatingsnow:shared_key@smoothalligator"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("@fascinatingsnow:shared_key@smoothalligator", keyStringUtil.getFullKeyName()); - assertEquals("shared_key", keyStringUtil.getKeyName()); - assertNull(keyStringUtil.getNamespace()); - assertEquals(KeyType.SHARED_KEY, keyStringUtil.getKeyType()); - assertEquals("@smoothalligator", keyStringUtil.getSharedBy()); - assertEquals("@fascinatingsnow", keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void saTest7() { - String KEY_NAME = - "atconnections.abbcservicesinc.smoothalligator.at_contact.mospherepro.abbcservicesinc@smoothalligator"; - KeyStringUtil keyStringUtil = new KeyStringUtil(KEY_NAME); - assertEquals("atconnections.abbcservicesinc.smoothalligator.at_contact.mospherepro.abbcservicesinc@smoothalligator", - keyStringUtil.getFullKeyName()); - assertEquals("atconnections.abbcservicesinc.smoothalligator.at_contact.mospherepro", keyStringUtil.getKeyName()); - assertEquals("abbcservicesinc", keyStringUtil.getNamespace()); - assertEquals(KeyType.SELF_KEY, keyStringUtil.getKeyType()); - assertEquals("@smoothalligator", keyStringUtil.getSharedBy()); - assertNull(keyStringUtil.getSharedWith()); - assertFalse(keyStringUtil.isCached()); - assertFalse(keyStringUtil.isHidden()); - } - - @Test - public void testGetNamespaceReturnsNullForKeyStringsWithNoNamespace() { - assertNamespace("public:location@alice", null); - assertNamespace("selfkey1@alice", null); - assertNamespace("@bob:phone@alice", null); - - assertNamespace("public:_hiddenlocation@alice", null); - assertNamespace("_hiddenselfkey1@alice", null); - assertNamespace("@bob:__hiddenphone@alice", null); - - assertNamespace("cached:@bob:phone@alice", null); - } - - @Test - public void testGetNamespaceReturnsNullForReservedSharedKeyPrefix() { - assertNamespace("shared_key.bob@alice", null); - } - - @Test - public void testGetNamespaceReturnsNamespaceForKeyStringWithNamespace() { - assertNamespace("public:location.ns@alice", "ns"); - assertNamespace("public:a.location.ns@alice", "ns"); - assertNamespace("a.b.selfkey1.ns@alice", "ns"); - assertNamespace("@bob:a.phone.ns@alice", "ns"); - assertNamespace("@bob:a.b.c.phone.ns@alice", "ns"); - - assertNamespace("public:_a.hiddenlocation.ns@alice", "ns"); - assertNamespace("_a.hiddenselfkey1.ns@alice", "ns"); - assertNamespace("@bob:__a.b.hiddenphone.ns@alice", "ns"); - } - - private static void assertNamespace(String fullKeyName, String expected) { - KeyStringUtil util = new KeyStringUtil(fullKeyName); - assertThat(util.getNamespace(), equalTo(expected)); - } - - private static void assertGetKeyNameAndGetNamespace(String fullKeyName, - String expectedKeyName, - String expectedNamespace) { - KeyStringUtil util = new KeyStringUtil(fullKeyName); - assertThat(util.getKeyName(), equalTo(expectedKeyName)); - assertThat(util.getNamespace(), equalTo(expectedNamespace)); - } - - -} diff --git a/at_client/src/test/java/org/atsign/common/KeysTest.java b/at_client/src/test/java/org/atsign/common/KeysTest.java new file mode 100644 index 00000000..d1263354 --- /dev/null +++ b/at_client/src/test/java/org/atsign/common/KeysTest.java @@ -0,0 +1,1277 @@ +package org.atsign.common; + +import static org.atsign.common.AtSign.createAtSign; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class KeysTest { + + @Test + void testPublicKeyBuilderThrowsExpectedExceptionWhenSharedByIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.publicKeyBuilder().build()); + assertThat(ex.getMessage(), containsString("sharedBy is not set")); + } + + @Test + void testPublicKeyBuilderThrowsExpectedExceptionWhenKeyNameIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.publicKeyBuilder().sharedBy(createAtSign("fred")).build()); + assertThat(ex.getMessage(), containsString("name is not set")); + } + + @Test + void testPublicKeyBuilderWithNoNamespaceCreatesExpectedKey() { + Keys.PublicKey key = Keys.publicKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .build(); + + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("public:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.TRUE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.FALSE) + .build())); + } + + @Test + void testPublicKeyBuilderWithNamespaceCreatesExpectedKey() { + Keys.PublicKey key = Keys.publicKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .namespace("ns") + .build(); + + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("key1.ns")); + assertThat(key.namespace(), equalTo("ns")); + assertThat(key.nameWithoutNamespace(), equalTo("key1")); + assertThat(key.toString(), equalTo("public:key1.ns@fred")); + } + + @Test + void testPublicKeyBuilderWithAdditionalMetadataFieldsCreatesExpectedKey() { + Keys.PublicKey key = Keys.publicKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .ttl(1000L) + .ttb(2000L) + .ttr(3000L) + .build(); + + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.TRUE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.FALSE) + .ttl(1000L) + .ttb(2000L) + .ttr(3000L) + .build())); + } + + @Test + void testPublicKeyBuilderWithMetadataFieldsOverridesCreatesExpectedKey() { + Keys.PublicKey key = Keys.publicKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .isCached(true) + .isBinary(true) + .build(); + + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.TRUE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.TRUE) + .isEncrypted(Boolean.FALSE) + .isBinary(Boolean.TRUE) + .build())); + assertThat(key.toString(), equalTo("cached:public:key1@fred")); + } + + @Test + void testKeyBuilderCreatesExpectedKeyForPublicKeys() { + Keys.AtKey key = Keys.keyBuilder().rawKey("public:key1@fred").build(); + + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("public:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.TRUE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.FALSE) + .build())); + + key = Keys.keyBuilder().rawKey("cached:public:key1@fred").build(); + + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("cached:public:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.TRUE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.TRUE) + .isEncrypted(Boolean.FALSE) + .build())); + + key = Keys.keyBuilder().rawKey("cached:public:_key1@fred").build(); + + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("_key1")); + assertThat(key.toString(), equalTo("cached:public:_key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.TRUE) + .isHidden(Boolean.TRUE) + .isCached(Boolean.TRUE) + .isEncrypted(Boolean.FALSE) + .build())); + + key = Keys.keyBuilder().rawKey("public:key1@fred").metadata(Metadata.builder().isEncrypted(true).build()).build(); + + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("public:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.TRUE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .build())); + } + + @Test + void testSelfKeyBuilderThrowsExpectedExceptionWhenSharedByIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.selfKeyBuilder().build()); + assertThat(ex.getMessage(), containsString("sharedBy is not set")); + } + + @Test + void testSelfKeyBuilderThrowsExpectedExceptionWhenKeyNameIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.selfKeyBuilder().sharedBy(createAtSign("fred")).build()); + assertThat(ex.getMessage(), containsString("name is not set")); + } + + @Test + void testSelfKeyBuilderWithNoNamespaceCreatesExpectedKey() { + Keys.SelfKey key = Keys.selfKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .build(); + + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .build())); + } + + @Test + void testSelfKeyBuilderWithNamespaceCreatesExpectedKey() { + Keys.SelfKey key = Keys.selfKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .namespace("ns") + .build(); + + assertThat(key.name(), equalTo("key1.ns")); + assertThat(key.toString(), equalTo("key1.ns@fred")); + assertThat(key.namespace(), equalTo("ns")); + assertThat(key.nameWithoutNamespace(), equalTo("key1")); + } + + @Test + void testSelfKeyBuilderWithSharedWithCreatesExpectedKey() { + Keys.SelfKey key = Keys.selfKeyBuilder() + .sharedWith(createAtSign("fred")) + .sharedBy(createAtSign("fred")) + .name("key1") + .namespace("ns") + .build(); + + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), equalTo(createAtSign("fred"))); + assertThat(key.toString(), equalTo("@fred:key1.ns@fred")); + assertThat(key.name(), equalTo("key1.ns")); + assertThat(key.namespace(), equalTo("ns")); + assertThat(key.nameWithoutNamespace(), equalTo("key1")); + } + + @Test + void testSelfKeyBuilderWithAdditionalMetadataFieldsCreatesExpectedKey() { + Keys.SelfKey key = Keys.selfKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .ttl(1000L) + .ttb(2000L) + .ttr(3000L) + .build(); + + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .ttl(1000L) + .ttb(2000L) + .ttr(3000L) + .build())); + } + + @Test + void testSelfKeyBuilderWithMetadataFieldsOverridesCreatesExpectedKey() { + Keys.SelfKey key = Keys.selfKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .isBinary(true) + .build(); + + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .isBinary(Boolean.TRUE) + .build())); + } + + @Test + void testKeyBuilderCreatesExpectedSelfKey() { + Keys.AtKey key = Keys.keyBuilder() + .rawKey("key1@fred") + .build(); + + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.toString(), equalTo("key1@fred")); + assertThat(key.name(), equalTo("key1")); + assertThat(key.namespace(), nullValue()); + assertThat(key.nameWithoutNamespace(), equalTo("key1")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .build())); + + key = Keys.keyBuilder() + .rawKey("key1.ns@fred") + .build(); + + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.toString(), equalTo("key1.ns@fred")); + assertThat(key.name(), equalTo("key1.ns")); + assertThat(key.namespace(), equalTo("ns")); + assertThat(key.nameWithoutNamespace(), equalTo("key1")); + + key = Keys.keyBuilder() + .rawKey("@fred:key1.ns@fred") + .build(); + + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), equalTo(createAtSign("fred"))); + assertThat(key.toString(), equalTo("@fred:key1.ns@fred")); + assertThat(key.name(), equalTo("key1.ns")); + assertThat(key.namespace(), equalTo("ns")); + assertThat(key.nameWithoutNamespace(), equalTo("key1")); + + key = Keys.keyBuilder() + .rawKey("@fred:key1.ns@fred") + .metadata(Metadata.builder().isBinary(true).build()) + .build(); + + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .isBinary(Boolean.TRUE) + .build())); + + } + + @Test + void testSharedKeyBuilderThrowsExpectedExceptionWhenSharedByIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.sharedKeyBuilder().build()); + assertThat(ex.getMessage(), containsString("sharedBy is not set")); + } + + @Test + void testSharedKeyBuilderThrowsExpectedExceptionWhenKeyNameIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.sharedKeyBuilder().sharedBy(createAtSign("fred")) + .sharedWith(createAtSign("colin")).build()); + assertThat(ex.getMessage(), containsString("name is not set")); + } + + @Test + void testSharedKeyBuilderThrowsExpectedExceptionWhenSharedKeyWithIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.sharedKeyBuilder().sharedBy(createAtSign("fred")).name("key1") + .build()); + assertThat(ex.getMessage(), containsString("sharedWith is not set")); + } + + @Test + void testSharedKeyBuilderThrowsExpectedExceptionWhenRawKeyAndOtherFieldsSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.sharedKeyBuilder().name("key1").rawKey("colin@:key1@fred") + .build()); + assertThat(ex.getMessage(), containsString("both rawKey and other fields set")); + } + + @Test + void testSharedKeyBuilderWithNoNamespaceCreatesExpectedKey() { + Keys.SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(createAtSign("fred")) + .sharedWith(createAtSign("colin")) + .name("key1") + .build(); + + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), equalTo(createAtSign("colin"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("@colin:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .build())); + } + + @Test + void testSharedKeyBuilderWithRawKeyNoNamespaceCreatesExpectedKey() { + Keys.SharedKey key = Keys.sharedKeyBuilder() + .rawKey("@colin:key1@fred") + .build(); + + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), equalTo(createAtSign("colin"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("@colin:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .build())); + } + + @Test + void testSharedKeyBuilderWithCachedRawKeyCreatesExpectedKey() { + Keys.SharedKey key = Keys.sharedKeyBuilder() + .rawKey("cached:@colin:key1@fred") + .build(); + + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), equalTo(createAtSign("colin"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("cached:@colin:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.TRUE) + .isEncrypted(Boolean.TRUE) + .build())); + } + + @Test + void testSharedKeyBuilderWithNamespaceCreatesExpectedKey() { + Keys.SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(createAtSign("fred")) + .sharedWith(createAtSign("colin")) + .name("key1") + .namespace("ns") + .build(); + + assertThat(key.name(), equalTo("key1.ns")); + assertThat(key.toString(), equalTo("@colin:key1.ns@fred")); + assertThat(key.namespace(), equalTo("ns")); + assertThat(key.nameWithoutNamespace(), equalTo("key1")); + } + + @Test + void testSharedKeyBuilderWithRawKeyWithNamespaceCreatesExpectedKey() { + Keys.SharedKey key = Keys.sharedKeyBuilder() + .rawKey("@colin:key1.ns@fred") + .build(); + + assertThat(key.name(), equalTo("key1.ns")); + assertThat(key.toString(), equalTo("@colin:key1.ns@fred")); + assertThat(key.namespace(), equalTo("ns")); + assertThat(key.nameWithoutNamespace(), equalTo("key1")); + } + + @Test + void testSharedKeyBuilderWithAdditionalMetadataFieldsCreatesExpectedKey() { + Keys.SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(createAtSign("fred")) + .sharedWith(createAtSign("colin")) + .name("key1") + .ttl(1000L) + .ttb(2000L) + .ttr(3000L) + .build(); + + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .ttl(1000L) + .ttb(2000L) + .ttr(3000L) + .build())); + } + + @Test + void testSharedKeyBuilderWithMetadataFieldsOverridesCreatesExpectedKey() { + Keys.SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(createAtSign("fred")) + .sharedWith(createAtSign("colin")) + .name("key1") + .isBinary(true) + .build(); + + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .isBinary(Boolean.TRUE) + .build())); + } + + @Test + void testKeyBuilderCreatesExpectedSharedKey() { + Keys.AtKey key = Keys.keyBuilder() + .rawKey("@colin:key1@fred") + .build(); + + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), equalTo(createAtSign("colin"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("@colin:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .build())); + + key = Keys.keyBuilder() + .rawKey("cached:@colin:key1@fred") + .build(); + + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), equalTo(createAtSign("colin"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("cached:@colin:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.TRUE) + .isEncrypted(Boolean.TRUE) + .build())); + + key = Keys.keyBuilder() + .rawKey("@colin:_key1@fred") + .build(); + + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), equalTo(createAtSign("colin"))); + assertThat(key.name(), equalTo("_key1")); + assertThat(key.toString(), equalTo("@colin:_key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.TRUE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .build())); + + key = Keys.keyBuilder() + .rawKey("@colin:key1@fred") + .metadata(Metadata.builder().isBinary(true).build()) + .build(); + + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.sharedWith(), equalTo(createAtSign("colin"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("@colin:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.FALSE) + .isCached(Boolean.FALSE) + .isEncrypted(Boolean.TRUE) + .isBinary(Boolean.TRUE) + .build())); + + } + + @Test + void testUpdateMissingMetadataWorksAsExpected() { + Keys.SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(createAtSign("fred")) + .sharedWith(createAtSign("colin")) + .name("key1") + .isBinary(true) + .ttl(1000L) + .build(); + + Metadata metadata = Metadata.builder() + .ttl(2000L) + .ttb(3000L) + .isBinary(false) + .build(); + + key.updateMissingMetadata(metadata); + + assertThat(key.metadata().ttl(), equalTo(1000L)); + assertThat(key.metadata().ttb(), equalTo(3000L)); + assertThat(key.metadata().isBinary(), equalTo(true)); + } + + @Test + void testOverwriteMetadataWorksAsExpected() { + Keys.SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(createAtSign("fred")) + .sharedWith(createAtSign("colin")) + .name("key1") + .isBinary(true) + .ttl(1000L) + .build(); + + Metadata metadata = Metadata.builder() + .ttl(2000L) + .ttb(3000L) + .isBinary(false) + .build(); + + key.overwriteMetadata(metadata); + + assertThat(key.metadata().ttl(), equalTo(2000L)); + assertThat(key.metadata().ttb(), equalTo(3000L)); + assertThat(key.metadata().isBinary(), equalTo(false)); + } + + @Test + void testPrivateHiddenKeyBuilderThrowsExpectedExceptionWhenSharedByIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.privateHiddenKeyBuilder().build()); + assertThat(ex.getMessage(), containsString("sharedBy is not set")); + } + + @Test + void testPrivateHiddenKeyBuilderThrowsExpectedExceptionWhenKeyNameIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> Keys.privateHiddenKeyBuilder().sharedBy(createAtSign("fred")) + .build()); + assertThat(ex.getMessage(), containsString("name is not set")); + } + + @Test + void testPrivateHiddenKeyBuilderWithNoNamespaceCreatesExpectedKey() { + Keys.PrivateHiddenKey key = Keys.privateHiddenKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .rawKey("private:key1@fred") + .build(); + + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("private:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.TRUE) + .isCached(Boolean.FALSE) + .build())); + } + + @Test + void testPrivateHiddenKeyBuilderWithNamespaceCreatesExpectedKey() { + Keys.PrivateHiddenKey key = Keys.privateHiddenKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key1") + .namespace("ns") + .rawKey("private:key1.ns@fred") + .build(); + + assertThat(key.name(), equalTo("key1.ns")); + assertThat(key.toString(), equalTo("private:key1.ns@fred")); + assertThat(key.namespace(), equalTo("ns")); + assertThat(key.nameWithoutNamespace(), equalTo("key1")); + } + + @Test + void testKeyBuilderCreatesExpectedPrivateHiddenKey() { + Keys.AtKey key = Keys.keyBuilder() + .rawKey("private:key1@fred") + .build(); + + assertThat(key, instanceOf(Keys.PrivateHiddenKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("private:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.TRUE) + .isCached(Boolean.FALSE) + .build())); + + key = Keys.keyBuilder() + .rawKey("private:key1@fred") + .metadata(Metadata.builder().isBinary(true).build()) + .build(); + + assertThat(key, instanceOf(Keys.PrivateHiddenKey.class)); + assertThat(key.sharedBy(), equalTo(createAtSign("fred"))); + assertThat(key.name(), equalTo("key1")); + assertThat(key.toString(), equalTo("private:key1@fred")); + assertThat(key.metadata(), equalTo(Metadata.builder() + .isPublic(Boolean.FALSE) + .isHidden(Boolean.TRUE) + .isCached(Boolean.FALSE) + .isBinary(Boolean.TRUE) + .build())); + } + + // + // Migrated KeyStringUtilTest.java + // + + @Test + public void testKeyBuilderWithPublicKeyRawKey() throws Exception { + Keys.AtKey key = Keys.keyBuilder().rawKey("public:phone@bob").build(); + + assertThat(key.toString(), equalTo("public:phone@bob")); + + assertThat(key.nameWithoutNamespace(), equalTo("phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + // Row 5 Public Hidden key + @Test + public void publicKey2() throws Exception { + String KEY_NAME = "public:_phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.toString(), equalTo("public:_phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("_phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(true)); + } + + // Row 6 Public Hidden key + @Test + public void publicKey3() throws Exception { + String KEY_NAME = "public:__phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.toString(), equalTo("public:__phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("__phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(true)); + } + + // Row 7A Public key and SharedWith populated + public void publicKey4A() throws Exception { + String KEY_NAME = "public:@bob:phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("public:@bob:phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith().toString(), equalTo("@bob")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + // Row 7B Public key and SharedWith populated + // TODO: query this scenario + @Test + public void publicKey4B() throws Exception { + String KEY_NAME = "public:@alice:phone@bob"; + IllegalArgumentException ex = + assertThrows(IllegalArgumentException.class, () -> Keys.keyBuilder().rawKey(KEY_NAME).build()); + assertThat(ex.getMessage(), containsString("does NOT match any raw key parser")); + } + + // Row 8 self key (sharedWith not populated) + @Test + public void selfKey1() throws Exception { + String KEY_NAME = "phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + // Row 9 Self key (sharedWith populated) + @Test + public void selfKey2() throws Exception { + String KEY_NAME = "@bob:phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@bob:phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith().toString(), equalTo("@bob")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + // Row 10 Self hidden key (sharedWith populated) + @Test + public void selfKey3() throws Exception { + String KEY_NAME = "@bob:_phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@bob:_phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("_phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith().toString(), equalTo("@bob")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(true)); + } + + // row 11 Self Hidden Key without sharedWith + @Test + public void selfKey4() throws Exception { + String KEY_NAME = "_phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("_phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("_phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(true)); + } + + // Row 12 SharedKey + @Test + public void sharedKey1() throws Exception { + String KEY_NAME = "@bob:phone@alice"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@bob:phone@alice")); + assertThat(key.nameWithoutNamespace(), equalTo("phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedWith().toString(), equalTo("@bob")); + assertThat(key.sharedBy().toString(), equalTo("@alice")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + // Row 13 Shared and hidden + @Test + public void sharedKey2() throws Exception { + String KEY_NAME = "@alice:_phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@alice:_phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("_phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith().toString(), equalTo("@alice")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(true)); + } + + // Row 14A Private keys + // TODO: query this scenario + @Test + public void privateKey1() throws Exception { + String KEY_NAME = "private:phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("private:phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.PrivateHiddenKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(true)); + assertThat(key.metadata().isPublic(), is(false)); + } + + // Row 14B Private keys + // TODO: query this scenario + @Test + public void privateKey2() throws Exception { + String KEY_NAME = "privatekey:phone@bob"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("privatekey:phone@bob")); + assertThat(key.nameWithoutNamespace(), equalTo("phone")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.PrivateHiddenKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@bob")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(true)); + assertThat(key.metadata().isPublic(), is(false)); + } + + /** + * 0: @farinataanxious:lemon@sportsunconscious + * 1: @farinataanxious:shared_key@sportsunconscious + * 2: @farinataanxious:test@sportsunconscious + * 3: @sportsunconscious:shared_key@sportsunconscious + * 4: @sportsunconscious:signing_privatekey@sportsunconscious + * 5: public:publickey@farinataanxious + * 6: public:publickey@sportsunconscious + * 7: public:signing_publickey@sportsunconscious + * 8: shared_key.farinataanxious@sportsunconscious + * 9: shared_key.sportsunconscious@sportsunconscious + */ + @Test + public void suKey1() throws Exception { + String KEY_NAME = "@farinataanxious:lemon@sportsunconscious"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@farinataanxious:lemon@sportsunconscious")); + assertThat(key.nameWithoutNamespace(), equalTo("lemon")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@sportsunconscious")); + assertThat(key.sharedWith().toString(), equalTo("@farinataanxious")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void suKey2() throws Exception { + String KEY_NAME = "@farinataanxious:shared_key@sportsunconscious"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@farinataanxious:shared_key@sportsunconscious")); + assertThat(key.namespace(), nullValue()); + assertThat(key.nameWithoutNamespace(), equalTo("shared_key")); + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@sportsunconscious")); + assertThat(key.sharedWith().toString(), equalTo("@farinataanxious")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void suKey3() throws Exception { + String KEY_NAME = "@farinataanxious:test@sportsunconscious"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@farinataanxious:test@sportsunconscious")); + assertThat(key.nameWithoutNamespace(), equalTo("test")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@sportsunconscious")); + assertThat(key.sharedWith().toString(), equalTo("@farinataanxious")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void suKey4() throws Exception { + String KEY_NAME = "@sportsunconscious:shared_key@sportsunconscious"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@sportsunconscious:shared_key@sportsunconscious")); + assertThat(key.nameWithoutNamespace(), equalTo("shared_key")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@sportsunconscious")); + assertThat(key.sharedWith().toString(), equalTo("@sportsunconscious")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void suKey5() throws Exception { + String KEY_NAME = "@sportsunconscious:signing_privatekey@sportsunconscious"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@sportsunconscious:signing_privatekey@sportsunconscious")); + assertThat(key.nameWithoutNamespace(), equalTo("signing_privatekey")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@sportsunconscious")); + assertThat(key.sharedWith().toString(), equalTo("@sportsunconscious")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void suKey6() throws Exception { + String KEY_NAME = "public:publickey@farinataanxious"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("public:publickey@farinataanxious")); + assertThat(key.nameWithoutNamespace(), equalTo("publickey")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@farinataanxious")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void suKey7() throws Exception { + String KEY_NAME = "public:publickey@sportsunconscious"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("public:publickey@sportsunconscious")); + assertThat(key.nameWithoutNamespace(), equalTo("publickey")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.PublicKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@sportsunconscious")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void suKey8() throws Exception { + String KEY_NAME = "shared_key.farinataanxious@sportsunconscious"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("shared_key.farinataanxious@sportsunconscious")); + assertThat(key.nameWithoutNamespace(), equalTo("shared_key.farinataanxious")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@sportsunconscious")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void suKey9() throws Exception { + String KEY_NAME = + "atconnections.hacktheleague.smoothalligator.at_contact.mospherepro.hacktheleague@smoothalligator"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertEquals("atconnections.hacktheleague.smoothalligator.at_contact.mospherepro.hacktheleague@smoothalligator", + key.toString()); + assertThat(key.nameWithoutNamespace(), + equalTo("atconnections.hacktheleague.smoothalligator.at_contact.mospherepro")); + assertThat(key.namespace(), equalTo("hacktheleague")); + + } + + /** + * smoothalligator + * 0: @abbcservicesinc:shared_key@smoothalligator + * 1: @denise:shared_key@smoothalligator + * 2: @er_nobile_14:shared_key@smoothalligator + * 3: @fascinatingsnow:shared_key@smoothalligator + * 4: @hacktheleague:shared_key@smoothalligator + * 5: @smoothalligator:signing_privatekey@smoothalligator + * 6: @wildgreen:shared_key@smoothalligator + * 7: + * atconnections.abbcservicesinc.smoothalligator.at_contact.mospherepro.abbcservicesinc@smoothalligator + * 8: atconnections.denise.smoothalligator.at_contact.mospherepro.denise@smoothalligator + * 9: + * atconnections.hacktheleague.smoothalligator.at_contact.mospherepro.hacktheleague@smoothalligator + * 10: atconnections.wildgreen.smoothalligator.at_contact.mospherepro.wildgreen@smoothalligator + * 11: @smoothalligator:shared_key@abbcservicesinc + * 12: @smoothalligator:shared_key@denise + * 13: @smoothalligator:shared_key@fascinatingsnow + * 14: @smoothalligator:shared_key@wildgreen + * 15: public:firstname.wavi.wavi@abbcservicesinc + * 16: public:firstname.wavi.wavi@wildgreen + * 17: public:image.wavi.wavi@abbcservicesinc + * 18: public:image.wavi.wavi@denise + * 19: public:image.wavi.wavi@wildgreen + * 20: public:lastname.wavi.wavi@abbcservicesinc + * 21: public:lastname.wavi.wavi@wildgreen + * 22: public:publickey@abbcservicesinc + * 23: public:publickey@denise + * 24: public:publickey@er_nobile_14 + * 25: public:publickey@fascinatingsnow + * 26: public:publickey@hacktheleague + * 27: public:publickey@wildgreen + * 28: public:email.wavi.wavi@smoothalligator + * 29: public:field_order_of_self.wavi.wavi@smoothalligator + * 30: public:firstname.wavi.wavi@smoothalligator + * 31: public:following_by_self.at_follows.wavi.at_follows@smoothalligator + * 32: public:lastname.wavi.wavi@smoothalligator + * 33: public:privateaccount.wavi.wavi@smoothalligator + * 34: public:publickey@smoothalligator + * 35: public:signing_publickey@smoothalligator + * 36: public:theme_color.wavi.wavi@smoothalligator + * 37: publickey.fascinatingsnow.fascinatingsnow@smoothalligator + * 38: senthistory_v2.mospherepro.mospherepro@smoothalligator + * 39: shared_key.abbcservicesinc@smoothalligator + * 40: shared_key.denise@smoothalligator + * 41: shared_key.er_nobile_14@smoothalligator + * 42: shared_key.fascinatingsnow@smoothalligator + * 43: shared_key.hacktheleague@smoothalligator + * 44: shared_key.wildgreen@smoothalligator + */ + + @Test + public void saTest1() throws Exception { + String KEY_NAME = "@abbcservicesinc:shared_key@smoothalligator"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@abbcservicesinc:shared_key@smoothalligator")); + assertThat(key.nameWithoutNamespace(), equalTo("shared_key")); + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@smoothalligator")); + assertThat(key.sharedWith().toString(), equalTo("@abbcservicesinc")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void saTest2() throws Exception { + String KEY_NAME = "@denise:shared_key@smoothalligator"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@denise:shared_key@smoothalligator")); + assertThat(key.nameWithoutNamespace(), equalTo("shared_key")); + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@smoothalligator")); + assertThat(key.sharedWith().toString(), equalTo("@denise")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void saTest3() throws Exception { + String KEY_NAME = "@er_nobile_14:shared_key@smoothalligator"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@er_nobile_14:shared_key@smoothalligator")); + assertThat(key.nameWithoutNamespace(), equalTo("shared_key")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@smoothalligator")); + assertThat(key.sharedWith().toString(), equalTo("@er_nobile_14")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + assertThat(key.namespace(), nullValue()); + } + + @Test + public void saTest4() throws Exception { + String KEY_NAME = "@fascinatingsnow:shared_key@smoothalligator"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertThat(key.toString(), equalTo("@fascinatingsnow:shared_key@smoothalligator")); + assertThat(key.nameWithoutNamespace(), equalTo("shared_key")); + assertThat(key.namespace(), nullValue()); + assertThat(key, instanceOf(Keys.SharedKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@smoothalligator")); + assertThat(key.sharedWith().toString(), equalTo("@fascinatingsnow")); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void saTest7() throws Exception { + String KEY_NAME = + "atconnections.abbcservicesinc.smoothalligator.at_contact.mospherepro.abbcservicesinc@smoothalligator"; + Keys.AtKey key = Keys.keyBuilder().rawKey(KEY_NAME).build(); + assertEquals("atconnections.abbcservicesinc.smoothalligator.at_contact.mospherepro.abbcservicesinc@smoothalligator", + key.toString()); + assertThat(key.nameWithoutNamespace(), + equalTo("atconnections.abbcservicesinc.smoothalligator.at_contact.mospherepro")); + assertThat(key.namespace(), equalTo("abbcservicesinc")); + assertThat(key, instanceOf(Keys.SelfKey.class)); + assertThat(key.sharedBy().toString(), equalTo("@smoothalligator")); + assertThat(key.sharedWith(), nullValue()); + assertThat(key.metadata().isCached(), is(false)); + assertThat(key.metadata().isHidden(), is(false)); + } + + @Test + public void testGetNamespaceReturnsNullForKeyStringsWithNoNamespace() throws Exception { + assertNamespace("public:location@alice", null); + assertNamespace("selfkey1@alice", null); + assertNamespace("@bob:phone@alice", null); + + assertNamespace("public:_hiddenlocation@alice", null); + assertNamespace("_hiddenselfkey1@alice", null); + assertNamespace("@bob:__hiddenphone@alice", null); + + assertNamespace("cached:@bob:phone@alice", null); + } + + @Test + public void testGetNamespaceReturnsNullForReservedSharedKeyPrefix() throws Exception { + assertNamespace("shared_key.bob@alice", null); + } + + @Test + public void testGetNamespaceReturnsNamespaceForKeyStringWithNamespace() throws Exception { + assertNamespace("public:location.ns@alice", "ns"); + assertNamespace("public:a.location.ns@alice", "ns"); + assertNamespace("a.b.selfkey1.ns@alice", "ns"); + assertNamespace("@bob:a.phone.ns@alice", "ns"); + assertNamespace("@bob:a.b.c.phone.ns@alice", "ns"); + + assertNamespace("public:_a.hiddenlocation.ns@alice", "ns"); + assertNamespace("_a.hiddenselfkey1.ns@alice", "ns"); + assertNamespace("@bob:__a.b.hiddenphone.ns@alice", "ns"); + } + + // + // migrated from AtClientValidationTest + // + + @Test + public void testInvalidKeyNameCauseExceptionsToBeThron() { + + Exception ex = assertThrows(IllegalArgumentException.class, + () -> Keys.publicKeyBuilder().sharedBy(createAtSign("fred")).name("").build()); + assertThat(ex.getMessage(), equalTo("key name is blank")); + + ex = assertThrows(IllegalArgumentException.class, + () -> Keys.publicKeyBuilder().sharedBy(createAtSign("fred")).name("test@").build()); + assertThat(ex.getMessage(), equalTo("illegal characters in key name")); + + ex = assertThrows(IllegalArgumentException.class, + () -> Keys.publicKeyBuilder().sharedBy(createAtSign("fred")).name("te st").build()); + assertThat(ex.getMessage(), equalTo("illegal characters in key name")); + + ex = assertThrows(IllegalArgumentException.class, + () -> Keys.publicKeyBuilder().sharedBy(createAtSign("fred")).name("te:st").build()); + assertThat(ex.getMessage(), equalTo("illegal characters in key name")); + } + + @Test + public void testPublicKeyInvalidMetadataThrowsException() { + Exception ex = assertThrows(IllegalArgumentException.class, () -> Keys.publicKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key") + .ttl(-1L) + .ttb(0L) + .ttr(-1L) + .build()); + assertThat(ex.getMessage(), equalTo("ttl cannot be negative")); + + ex = assertThrows(IllegalArgumentException.class, () -> Keys.publicKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key") + .ttl(0L) + .ttb(-1L) + .ttr(-1L) + .build()); + assertThat(ex.getMessage(), equalTo("ttb cannot be negative")); + + ex = assertThrows(IllegalArgumentException.class, () -> Keys.publicKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key") + .ttl(0L) + .ttb(0L) + .ttr(-2L) + .build()); + assertThat(ex.getMessage(), equalTo("ttr cannot be < -1")); + } + + @Test + public void testSelfKeyInvalidMetadataThrowsException() { + Exception ex = assertThrows(IllegalArgumentException.class, () -> Keys.selfKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key") + .ttl(-1L) + .ttb(0L) + .ttr(-1L) + .build()); + assertThat(ex.getMessage(), equalTo("ttl cannot be negative")); + + ex = assertThrows(IllegalArgumentException.class, () -> Keys.selfKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key") + .ttl(0L) + .ttb(-1L) + .ttr(-1L) + .build()); + assertThat(ex.getMessage(), equalTo("ttb cannot be negative")); + + ex = assertThrows(IllegalArgumentException.class, () -> Keys.selfKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("key") + .ttl(0L) + .ttb(0L) + .ttr(-2L) + .build()); + assertThat(ex.getMessage(), equalTo("ttr cannot be < -1")); + } + + @Test + public void testSharedKeyInvalidMetadataThrowsException() { + Exception ex = assertThrows(IllegalArgumentException.class, () -> Keys.sharedKeyBuilder() + .sharedWith(createAtSign("colin")) + .sharedBy(createAtSign("fred")) + .name("key") + .ttl(-1L) + .ttb(0L) + .ttr(-1L) + .build()); + assertThat(ex.getMessage(), equalTo("ttl cannot be negative")); + + ex = assertThrows(IllegalArgumentException.class, () -> Keys.sharedKeyBuilder() + .sharedWith(createAtSign("colin")) + .sharedBy(createAtSign("fred")) + .name("key") + .ttl(0L) + .ttb(-1L) + .ttr(-1L) + .build()); + assertThat(ex.getMessage(), equalTo("ttb cannot be negative")); + + ex = assertThrows(IllegalArgumentException.class, () -> Keys.sharedKeyBuilder() + .sharedWith(createAtSign("colin")) + .sharedBy(createAtSign("fred")) + .name("key") + .ttl(0L) + .ttb(0L) + .ttr(-2L) + .build()); + assertThat(ex.getMessage(), equalTo("ttr cannot be < -1")); + } + + private static void assertNamespace(String fullKeyName, String expected) { + Keys.AtKey key = Keys.keyBuilder().rawKey(fullKeyName).build(); + assertThat(key.namespace(), equalTo(expected)); + } + +} diff --git a/at_client/src/test/java/org/atsign/common/KeysUtilTest.java b/at_client/src/test/java/org/atsign/common/KeysUtilTest.java index 21fe86c6..e541ff58 100644 --- a/at_client/src/test/java/org/atsign/common/KeysUtilTest.java +++ b/at_client/src/test/java/org/atsign/common/KeysUtilTest.java @@ -30,10 +30,11 @@ public void testSaveKeysFile() throws Exception { assertFalse(expected.exists()); // Given a Map of keys (like Onboard creates) - AtKeys keys = new AtKeys() - .setEncryptKeyPair(generateRSAKeyPair()) - .setApkamKeyPair(generateRSAKeyPair()) - .setSelfEncryptKey(generateAESKeyBase64()); + AtKeys keys = AtKeys.builder() + .encryptKeyPair(generateRSAKeyPair()) + .apkamKeyPair(generateRSAKeyPair()) + .selfEncryptKey(generateAESKeyBase64()) + .build(); // When we call KeysUtil.saveKeys KeysUtil.saveKeys(testAtSign, keys); @@ -45,10 +46,11 @@ public void testSaveKeysFile() throws Exception { @Test public void testLoadKeysFile() throws Exception { // Given a correctly formatted keys file in the canonical location - AtKeys keys = new AtKeys() - .setEncryptKeyPair(generateRSAKeyPair()) - .setApkamKeyPair(generateRSAKeyPair()) - .setSelfEncryptKey(generateAESKeyBase64()); + AtKeys keys = AtKeys.builder() + .encryptKeyPair(generateRSAKeyPair()) + .apkamKeyPair(generateRSAKeyPair()) + .selfEncryptKey(generateAESKeyBase64()) + .build(); KeysUtil.saveKeys(testAtSign, keys); // When we call KeysUtil.loadKeys @@ -60,10 +62,11 @@ public void testLoadKeysFile() throws Exception { @Test public void testLoadKeysFileLegacy() throws Exception { - AtKeys keys = new AtKeys() - .setEncryptKeyPair(generateRSAKeyPair()) - .setApkamKeyPair(generateRSAKeyPair()) - .setSelfEncryptKey(generateAESKeyBase64()); + AtKeys keys = AtKeys.builder() + .encryptKeyPair(generateRSAKeyPair()) + .apkamKeyPair(generateRSAKeyPair()) + .selfEncryptKey(generateAESKeyBase64()) + .build(); KeysUtil.saveKeys(testAtSign, keys); File expected = KeysUtil.getKeysFile(testAtSign, KeysUtil.expectedKeysFilesLocation); diff --git a/at_client/src/test/java/org/atsign/common/MetadataTest.java b/at_client/src/test/java/org/atsign/common/MetadataTest.java new file mode 100644 index 00000000..3fe98762 --- /dev/null +++ b/at_client/src/test/java/org/atsign/common/MetadataTest.java @@ -0,0 +1,453 @@ +package org.atsign.common; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class MetadataTest { + + @Test + void testToStringReturnsEmptyStringWhenAllNull() { + Metadata md = Metadata.builder().build(); + assertThat(md.toString(), is("")); + } + + @Test + void testToStringIncludesTtlWhenSet() { + Metadata md = Metadata.builder().ttl(500L).build(); + assertThat(md.toString(), containsString(":ttl:500")); + } + + @Test + void testToStringIncludesTtbWhenSet() { + Metadata md = Metadata.builder().ttb(200L).build(); + assertThat(md.toString(), containsString(":ttb:200")); + } + + @Test + void testToStringIncludesTtrWhenSet() { + Metadata md = Metadata.builder().ttr(100L).build(); + assertThat(md.toString(), containsString(":ttr:100")); + } + + @Test + void testToStringIncludesCcdWhenSet() { + Metadata md = Metadata.builder().ccd(true).build(); + assertThat(md.toString(), containsString(":ccd:true")); + } + + @Test + void testToStringIncludesDataSignatureWhenSet() { + Metadata md = Metadata.builder().dataSignature("sig123").build(); + assertThat(md.toString(), containsString(":dataSignature:sig123")); + } + + @Test + void testToStringIncludesSharedKeyStatusWhenSet() { + Metadata md = Metadata.builder().sharedKeyStatus("cached").build(); + assertThat(md.toString(), containsString(":sharedKeyStatus:cached")); + } + + @Test + void testToStringIncludesSharedKeyEncWhenSet() { + Metadata md = Metadata.builder().sharedKeyEnc("encKey").build(); + assertThat(md.toString(), containsString(":sharedKeyEnc:encKey")); + } + + @Test + void testToStringIncludesPubKeyCSWhenSet() { + Metadata md = Metadata.builder().pubKeyCS("checksum").build(); + assertThat(md.toString(), containsString(":pubKeyCS:checksum")); + } + + @Test + void testToStringIncludesIsBinaryWhenSet() { + Metadata md = Metadata.builder().isBinary(false).build(); + assertThat(md.toString(), containsString(":isBinary:false")); + } + + @Test + void testToStringIncludesIsEncryptedWhenSet() { + Metadata md = Metadata.builder().isEncrypted(true).build(); + assertThat(md.toString(), containsString(":isEncrypted:true")); + } + + @Test + void testToStringIncludesEncodingWhenSet() { + Metadata md = Metadata.builder().encoding("base64").build(); + assertThat(md.toString(), containsString(":encoding:base64")); + } + + @Test + void testToStringIncludesIvNonceWhenSet() { + Metadata md = Metadata.builder().ivNonce("nonce42").build(); + assertThat(md.toString(), containsString(":ivNonce:nonce42")); + } + + @Test + void testToStringOmitsFieldsNotSet() { + Metadata md = Metadata.builder().ttl(1L).build(); + String s = md.toString(); + assertThat(s, not(containsString(":ttb:"))); + assertThat(s, not(containsString(":ttr:"))); + assertThat(s, not(containsString(":encoding:"))); + } + + @Test + void testFromJsonParsesBasicFields() throws JsonProcessingException { + String json = "{\"ttl\":300,\"isPublic\":true,\"encoding\":\"base64\"}"; + Metadata md = Metadata.fromJson(json); + + assertThat(md.ttl(), is(300L)); + assertThat(md.isPublic(), is(true)); + assertThat(md.encoding(), is("base64")); + } + + @Test + void testFromJsonEmptyObjectProducesAllNullFields() throws JsonProcessingException { + Metadata md = Metadata.fromJson("{}"); + assertThat(md.ttl(), is(nullValue())); + assertThat(md.isPublic(), is(nullValue())); + assertThat(md.encoding(), is(nullValue())); + } + + @Test + void testFromJsonInvalidJsonThrowsException() { + assertThrows(JsonProcessingException.class, () -> Metadata.fromJson("not-json")); + } + + @Test + void testMergeMd1FieldsTakePriorityOverMd2() { + Metadata md1 = Metadata.builder() + .ttl(1L).ttb(2L).ttr(3L).ccd(true) + .isPublic(true).isHidden(true).isCached(true) + .isEncrypted(true).isBinary(true).namespaceAware(true) + .dataSignature("ds1").sharedKeyStatus("sks1").sharedKeyEnc("ske1") + .pubKeyCS("pkcs1").encoding("utf8").ivNonce("iv1") + .build(); + + Metadata md2 = Metadata.builder() + .ttl(99L).ttb(99L).ttr(99L).ccd(false) + .isPublic(false).isHidden(false).isCached(false) + .isEncrypted(false).isBinary(false).namespaceAware(false) + .dataSignature("ds2").sharedKeyStatus("sks2").sharedKeyEnc("ske2") + .pubKeyCS("pkcs2").encoding("ascii").ivNonce("iv2") + .build(); + + Metadata merged = Metadata.merge(md1, md2); + + assertThat(merged.ttl(), is(1L)); + assertThat(merged.ttb(), is(2L)); + assertThat(merged.ttr(), is(3L)); + assertThat(merged.ccd(), is(true)); + assertThat(merged.isPublic(), is(true)); + assertThat(merged.isHidden(), is(true)); + assertThat(merged.isCached(), is(true)); + assertThat(merged.isEncrypted(), is(true)); + assertThat(merged.isBinary(), is(true)); + assertThat(merged.namespaceAware(), is(true)); + assertThat(merged.dataSignature(), is("ds1")); + assertThat(merged.sharedKeyStatus(), is("sks1")); + assertThat(merged.sharedKeyEnc(), is("ske1")); + assertThat(merged.pubKeyCS(), is("pkcs1")); + assertThat(merged.encoding(), is("utf8")); + assertThat(merged.ivNonce(), is("iv1")); + } + + @Test + void testMergeMd2FieldsUsedWhenMd1FieldsAreNull() { + Metadata md1 = Metadata.builder().build(); + + OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC); + Metadata md2 = Metadata.builder() + .ttl(1L).ttb(2L).ttr(3L).ccd(true) + .isPublic(true).isHidden(true).isCached(true) + .isEncrypted(true).isBinary(true).namespaceAware(true) + .dataSignature("ds2").sharedKeyStatus("sks2").sharedKeyEnc("ske2") + .pubKeyCS("pkcs2").encoding("ascii").ivNonce("iv2") + .availableAt(now).expiresAt(now).refreshAt(now) + .createdAt(now).updatedAt(now) + .build(); + + Metadata merged = Metadata.merge(md1, md2); + + assertThat(merged.ttl(), is(1L)); + assertThat(merged.ttb(), is(2L)); + assertThat(merged.ttr(), is(3L)); + assertThat(merged.ccd(), is(true)); + assertThat(merged.isPublic(), is(true)); + assertThat(merged.isHidden(), is(true)); + assertThat(merged.isCached(), is(true)); + assertThat(merged.isEncrypted(), is(true)); + assertThat(merged.isBinary(), is(true)); + assertThat(merged.namespaceAware(), is(true)); + assertThat(merged.dataSignature(), is("ds2")); + assertThat(merged.sharedKeyStatus(), is("sks2")); + assertThat(merged.sharedKeyEnc(), is("ske2")); + assertThat(merged.pubKeyCS(), is("pkcs2")); + assertThat(merged.encoding(), is("ascii")); + assertThat(merged.ivNonce(), is("iv2")); + assertThat(merged.availableAt(), is(now)); + assertThat(merged.expiresAt(), is(now)); + assertThat(merged.refreshAt(), is(now)); + assertThat(merged.createdAt(), is(now)); + assertThat(merged.updatedAt(), is(now)); + } + + @Test + void testMergeFieldRemainsNullWhenBothMd1AndMd2AreNull() { + Metadata merged = Metadata.merge(Metadata.builder().build(), Metadata.builder().build()); + + assertThat(merged.ttl(), is(nullValue())); + assertThat(merged.ttb(), is(nullValue())); + assertThat(merged.ttr(), is(nullValue())); + assertThat(merged.ccd(), is(nullValue())); + assertThat(merged.isPublic(), is(nullValue())); + assertThat(merged.isHidden(), is(nullValue())); + assertThat(merged.isCached(), is(nullValue())); + assertThat(merged.isEncrypted(), is(nullValue())); + assertThat(merged.isBinary(), is(nullValue())); + assertThat(merged.namespaceAware(), is(nullValue())); + assertThat(merged.dataSignature(), is(nullValue())); + assertThat(merged.sharedKeyStatus(), is(nullValue())); + assertThat(merged.sharedKeyEnc(), is(nullValue())); + assertThat(merged.pubKeyCS(), is(nullValue())); + assertThat(merged.encoding(), is(nullValue())); + assertThat(merged.ivNonce(), is(nullValue())); + assertThat(merged.availableAt(), is(nullValue())); + assertThat(merged.expiresAt(), is(nullValue())); + assertThat(merged.refreshAt(), is(nullValue())); + assertThat(merged.createdAt(), is(nullValue())); + assertThat(merged.updatedAt(), is(nullValue())); + } + + @Test + void testSetTtlIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setTtlIfNotNull(b, 42L), is(true)); + assertThat(b.build().ttl(), is(42L)); + } + + @Test + void testSetTtlIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().ttl(42L); + assertThat(Metadata.setTtlIfNotNull(b, null), is(false)); + assertThat(b.build().ttl(), is(42L)); + } + + @Test + void testSetTtbIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setTtbIfNotNull(b, 10L), is(true)); + assertThat(b.build().ttb(), is(10L)); + } + + @Test + void testSetTtbIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().ttb(10L); + assertThat(Metadata.setTtbIfNotNull(b, null), is(false)); + assertThat(b.build().ttb(), is(10L)); + } + + @Test + void testSetTtrIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setTtrIfNotNull(b, 5L), is(true)); + assertThat(b.build().ttr(), is(5L)); + } + + @Test + void testSetTtrIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().ttr(5L); + assertThat(Metadata.setTtrIfNotNull(b, null), is(false)); + assertThat(b.build().ttr(), is(5L)); + } + + @Test + void testSetCcdIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setCcdIfNotNull(b, false), is(true)); + assertThat(b.build().ccd(), is(false)); + } + + @Test + void testSetCcdIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().ccd(true); + assertThat(Metadata.setCcdIfNotNull(b, null), is(false)); + assertThat(b.build().ccd(), is(true)); + } + + @Test + void testSetIsPublicIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setIsPublicIfNotNull(b, true), is(true)); + assertThat(b.build().isPublic(), is(true)); + } + + @Test + void testSetIsPublicIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().isPublic(true); + assertThat(Metadata.setIsPublicIfNotNull(b, null), is(false)); + assertThat(b.build().isPublic(), is(true)); + } + + @Test + void testSetIsHiddenIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setIsHiddenIfNotNull(b, true), is(true)); + assertThat(b.build().isHidden(), is(true)); + } + + @Test + void testSetIsHiddenIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().isHidden(true); + assertThat(Metadata.setIsHiddenIfNotNull(b, null), is(false)); + assertThat(b.build().isHidden(), is(true)); + } + + @Test + void testSetIsCachedIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setIsCachedIfNotNull(b, false), is(true)); + assertThat(b.build().isCached(), is(false)); + } + + @Test + void testSetIsCachedIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().isCached(true); + assertThat(Metadata.setIsCachedIfNotNull(b, null), is(false)); + assertThat(b.build().isCached(), is(true)); + } + + @Test + void testSetIsBinaryIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setIsBinaryIfNotNull(b, true), is(true)); + assertThat(b.build().isBinary(), is(true)); + } + + @Test + void testSetIsBinaryIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().isBinary(true); + assertThat(Metadata.setIsBinaryIfNotNull(b, null), is(false)); + assertThat(b.build().isBinary(), is(true)); + } + + @Test + void testSetIsEncryptedIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setIsEncryptedIfNotNull(b, false), is(true)); + assertThat(b.build().isEncrypted(), is(false)); + } + + @Test + void testSetIsEncryptedIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().isEncrypted(true); + assertThat(Metadata.setIsEncryptedIfNotNull(b, null), is(false)); + assertThat(b.build().isEncrypted(), is(true)); + } + + @Test + void testSetNamespaceAwareIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setNamespaceAwareIfNotNull(b, true), is(true)); + assertThat(b.build().namespaceAware(), is(true)); + } + + @Test + void testSetNamespaceAwareIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().namespaceAware(true); + assertThat(Metadata.setNamespaceAwareIfNotNull(b, null), is(false)); + assertThat(b.build().namespaceAware(), is(true)); + } + + @Test + void testSetDataSignatureIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setDataSignatureIfNotNull(b, "sig"), is(true)); + assertThat(b.build().dataSignature(), is("sig")); + } + + @Test + void testSetDataSignatureIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().dataSignature("sig"); + assertThat(Metadata.setDataSignatureIfNotNull(b, null), is(false)); + assertThat(b.build().dataSignature(), is("sig")); + } + + @Test + void testSetSharedKeyStatusIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setSharedKeyStatusIfNotNull(b, "ok"), is(true)); + assertThat(b.build().sharedKeyStatus(), is("ok")); + } + + @Test + void testSetSharedKeyStatusIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().sharedKeyStatus("ok"); + assertThat(Metadata.setSharedKeyStatusIfNotNull(b, null), is(false)); + assertThat(b.build().sharedKeyStatus(), is("ok")); + } + + @Test + void testSetSharedKeyEncIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setSharedKeyEncIfNotNull(b, "encKey"), is(true)); + assertThat(b.build().sharedKeyEnc(), is("encKey")); + } + + @Test + void testSetSharedKeyEncIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().sharedKeyEnc("encKey"); + assertThat(Metadata.setSharedKeyEncIfNotNull(b, null), is(false)); + assertThat(b.build().sharedKeyEnc(), is("encKey")); + } + + @Test + void testSetPubKeyCSIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setPubKeyCSIfNotNull(b, "checksum"), is(true)); + assertThat(b.build().pubKeyCS(), is("checksum")); + } + + @Test + void testSetPubKeyCSIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().pubKeyCS("checksum"); + assertThat(Metadata.setPubKeyCSIfNotNull(b, null), is(false)); + assertThat(b.build().pubKeyCS(), is("checksum")); + } + + @Test + void testSetEncodingIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setEncodingIfNotNull(b, "utf8"), is(true)); + assertThat(b.build().encoding(), is("utf8")); + } + + @Test + void testSetEncodingIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().encoding("utf8"); + assertThat(Metadata.setEncodingIfNotNull(b, null), is(false)); + assertThat(b.build().encoding(), is("utf8")); + } + + @Test + void testSetIvNonceIfNotNullReturnsTrueAndSetsValueWhenNotNull() { + Metadata.MetadataBuilder b = Metadata.builder(); + assertThat(Metadata.setIvNonceIfNotNull(b, "iv99"), is(true)); + assertThat(b.build().ivNonce(), is("iv99")); + } + + @Test + void testSetIvNonceIfNotNullReturnsFalseAndDoesNotOverwriteExistingValueWhenNull() { + Metadata.MetadataBuilder b = Metadata.builder().ivNonce("iv99"); + assertThat(Metadata.setIvNonceIfNotNull(b, null), is(false)); + assertThat(b.build().ivNonce(), is("iv99")); + } +} diff --git a/at_client/src/test/java/org/atsign/common/ResponseTransformerTest.java b/at_client/src/test/java/org/atsign/common/ResponseTransformerTest.java index caefc7c3..a69cf88c 100644 --- a/at_client/src/test/java/org/atsign/common/ResponseTransformerTest.java +++ b/at_client/src/test/java/org/atsign/common/ResponseTransformerTest.java @@ -12,7 +12,7 @@ import static org.junit.jupiter.api.Assertions.*; public class ResponseTransformerTest { - final ObjectMapper mapper = new ObjectMapper(); + final ObjectMapper mapper = Json.MAPPER; final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS'Z'"); @Test @@ -59,25 +59,25 @@ public void llookupAllResponseTransformerTest() throws JsonProcessingException { assertEquals( "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCz9nTBDDLxLgSxYu4+mDF3anWuTlKysXBBLsp3glrBP9xEXDx4muOHuHZIzuNvFlsjcCDF/mLSAJcvbxUoTsOQp+QD5XMhlNS9TWGsmNks7KHylNEhcqo2Va7RZxNS6MZBRacl+OusnebVKdOXDnbuevoED5fSklOz7mvdm9Mb2wIDAQAB", model.data); - assertNull(model.metaData.createdBy); - assertNull(model.metaData.updatedBy); + assertNull(model.metaData.createdBy()); + assertNull(model.metaData.updatedBy()); - assertEquals("2022-08-12 01:50:15.398Z", dateTimeFormatter.format(model.metaData.createdAt)); - assertEquals("2022-08-12 01:50:15.398Z", dateTimeFormatter.format(model.metaData.updatedAt)); - assertEquals("2022-08-12 01:50:15.398Z", dateTimeFormatter.format(model.metaData.availableAt)); - assertNull(model.metaData.expiresAt); - assertNull(model.metaData.refreshAt); - assertEquals("active", model.metaData.status); - assertEquals(0, (int) model.metaData.version); - assertEquals(0, (int) model.metaData.ttl); - assertEquals(0, (int) model.metaData.ttb); - assertNull(model.metaData.ttr); - assertNull(model.metaData.ccd); - assertFalse(model.metaData.isBinary); - assertFalse(model.metaData.isEncrypted); - assertNull(model.metaData.dataSignature); - assertNull(model.metaData.sharedKeyEnc); - assertNull(model.metaData.pubKeyCS); - assertNull(model.metaData.encoding); + assertEquals("2022-08-12 01:50:15.398Z", dateTimeFormatter.format(model.metaData.createdAt())); + assertEquals("2022-08-12 01:50:15.398Z", dateTimeFormatter.format(model.metaData.updatedAt())); + assertEquals("2022-08-12 01:50:15.398Z", dateTimeFormatter.format(model.metaData.availableAt())); + assertNull(model.metaData.expiresAt()); + assertNull(model.metaData.refreshAt()); + assertEquals("active", model.metaData.status()); + assertEquals(0, (int) model.metaData.version()); + assertEquals(0, model.metaData.ttl()); + assertEquals(0, model.metaData.ttb()); + assertNull(model.metaData.ttr()); + assertNull(model.metaData.ccd()); + assertFalse(model.metaData.isBinary()); + assertFalse(model.metaData.isEncrypted()); + assertNull(model.metaData.dataSignature()); + assertNull(model.metaData.sharedKeyEnc()); + assertNull(model.metaData.pubKeyCS()); + assertNull(model.metaData.encoding()); } } diff --git a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java index a690603f..1a683003 100644 --- a/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java +++ b/at_client/src/test/java/org/atsign/common/VerbBuildersTest.java @@ -1,291 +1,550 @@ package org.atsign.common; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.atsign.client.util.EnrollmentId.createEnrollmentId; +import static org.atsign.common.AtSign.createAtSign; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.atsign.client.api.AtKeyNames; import org.atsign.common.Keys.PublicKey; import org.atsign.common.Keys.SelfKey; import org.atsign.common.Keys.SharedKey; -import org.atsign.common.VerbBuilders.PlookupVerbBuilder.Type; -import org.atsign.common.VerbBuilders.*; +import org.atsign.common.VerbBuilders.EnrollOperation; +import org.atsign.common.VerbBuilders.LookupOperation; +import org.atsign.common.VerbBuilders.NotifyOperation; +import org.atsign.common.VerbBuilders.UpdateCommandBuilder; import org.junit.jupiter.api.Test; public class VerbBuildersTest { @Test - public void fromVerbBuilderTest() { - FromVerbBuilder builder; - String command; + public void testFromBuilderGeneratesExpectedOutput() { - builder = new FromVerbBuilder(); - builder.setAtSign("@bob"); - command = builder.build(); // "from:@bob" + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.fromCommandBuilder().build()); + assertThat(ex.getMessage(), containsString("atSign not set")); + + String command = VerbBuilders.fromCommandBuilder() + .atSign(createAtSign("@bob")) + .build(); assertEquals("from:@bob", command); } @Test - public void cramVerbBuilderTest() { - CRAMVerbBuilder builder; - String command; + public void testCramBuilderGeneratesExpectedOutput() { + + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.cramCommandBuilder().build()); + assertThat(ex.getMessage(), containsString("digest not set")); - builder = new CRAMVerbBuilder(); - builder.setDigest("digest"); - command = builder.build(); // "cram:digest" + String command = VerbBuilders.cramCommandBuilder() + .digest("digest") + .build(); assertEquals("cram:digest", command); } @Test - public void polVerbBuilderTest() { - POLVerbBuilder builder; - String command; - - builder = new POLVerbBuilder(); - command = builder.build(); // "pol" + public void testPolBuilderGeneratesExpectedOutput() { + String command = VerbBuilders.polCommandBuilder().build(); assertEquals("pol", command); } @Test - public void pkamVerbBuilderTest() { - PKAMVerbBuilder builder; - String command; + public void testPkamBuilderGeneratesExpectedOutput() { - builder = new PKAMVerbBuilder(); - builder.setDigest("digest"); - command = builder.build(); // "pkam:digest" + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.pkamCommandBuilder().build()); + assertThat(ex.getMessage(), containsString("digest not set")); + + String command = VerbBuilders.pkamCommandBuilder() + .digest("digest") + .build(); assertEquals("pkam:digest", command); } @Test - public void updateVerbBuilderTest() { - UpdateVerbBuilder builder; + public void testPkamBuilderGeneratesExpectedOutputWhenEnrollmentIdIsSet() { + + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.pkamCommandBuilder() + .digest("digest") + .enrollmentId(createEnrollmentId("12345-6789")) + .signingAlgo("RSA") + .build()); + assertThat(ex.getMessage(), containsString("hashingAlgo not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.pkamCommandBuilder() + .digest("digest") + .enrollmentId(createEnrollmentId("12345-6789")) + .hashingAlgo("SHA") + .build()); + assertThat(ex.getMessage(), containsString("signingAlgo not set")); + + String command = VerbBuilders.pkamCommandBuilder() + .digest("digest") + .enrollmentId(createEnrollmentId("12345-6789")) + .signingAlgo("RSA") + .hashingAlgo("SHA") + .build(); + assertEquals("pkam:signingAlgo:RSA:hashingAlgo:SHA:enrollmentId:12345-6789:digest", command); + } + + @Test + public void testUpdateBuilderThrowsExceptionsWhenMandatoryFieldsAreNotSet() { + + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.updateCommandBuilder().build()); + assertThat(ex.getMessage(), containsString("keyName not set")); + + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.updateCommandBuilder().keyName("test").build()); + assertThat(ex.getMessage(), containsString("sharedBy not set")); + + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.updateCommandBuilder().keyName("test").sharedBy(createAtSign("fred")).build()); + assertThat(ex.getMessage(), containsString("value not set")); + + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.updateCommandBuilder().isPublic(true).rawKey("@colin:key1@fred").build()); + assertThat(ex.getMessage(), containsString("both rawKeys and isHidden, isPublic isCached set")); + + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.updateCommandBuilder().keyName("test").rawKey("@colin:key1@fred").build()); + assertThat(ex.getMessage(), containsString("both rawKeys and key fields set")); + } + + @Test + public void testUpdateBuilderMetadataSettersOverrideKeyMetadata() { + PublicKey key = Keys.publicKeyBuilder().sharedBy(createAtSign("fred")).name("test").build(); + UpdateCommandBuilder builder = VerbBuilders.updateCommandBuilder().key(key).value("x"); + + builder = VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).value("x"); + assertThat(builder.isPublic(true).build(), + equalTo("update:public:test@fred x")); + assertThat(builder.isPublic(false).build(), + equalTo("update:test@fred x")); + + builder = VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).value("x"); + assertThat(builder.isHidden(true).build(), + equalTo("update:_test@fred x")); + assertThat(builder.isHidden(false).build(), + equalTo("update:test@fred x")); + + builder = VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).value("x"); + assertThat(builder.isCached(true).build(), + equalTo("update:cached:test@fred x")); + assertThat(builder.isCached(false).build(), + equalTo("update:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.ttl(SECONDS.toMillis(1)).build(), + equalTo("update:ttl:1000:public:test@fred x")); + assertThat(builder.ttl(null).build(), + equalTo("update:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.ttb(SECONDS.toMillis(2)).build(), + equalTo("update:ttb:2000:public:test@fred x")); + assertThat(builder.ttb(null).build(), + equalTo("update:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.ttr(SECONDS.toMillis(3)).build(), + equalTo("update:ttr:3000:public:test@fred x")); + assertThat(builder.ttr(null).build(), + equalTo("update:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.ccd(true).build(), + equalTo("update:ccd:true:public:test@fred x")); + assertThat(builder.ccd(false).build(), + equalTo("update:ccd:false:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.isBinary(true).build(), + equalTo("update:isBinary:true:public:test@fred x")); + assertThat(builder.isBinary(false).build(), + equalTo("update:isBinary:false:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.isEncrypted(true).build(), + equalTo("update:isEncrypted:true:public:test@fred x")); + assertThat(builder.isEncrypted(false).build(), + equalTo("update:isEncrypted:false:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.dataSignature("XYZ").build(), + equalTo("update:dataSignature:XYZ:public:test@fred x")); + assertThat(builder.dataSignature(null).build(), + equalTo("update:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.sharedKeyEnc("abcdef").build(), + equalTo("update:sharedKeyEnc:abcdef:public:test@fred x")); + assertThat(builder.sharedKeyEnc(null).build(), + equalTo("update:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.pubKeyCS("xxxx").build(), + equalTo("update:pubKeyCS:xxxx:public:test@fred x")); + assertThat(builder.pubKeyCS(null).build(), + equalTo("update:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.encoding("en").build(), + equalTo("update:encoding:en:public:test@fred x")); + assertThat(builder.encoding(null).build(), + equalTo("update:public:test@fred x")); + + builder = + VerbBuilders.updateCommandBuilder().keyName(key.name()).sharedBy(key.sharedBy()).isPublic(true).value("x"); + assertThat(builder.ivNonce("abc123op").build(), + equalTo("update:ivNonce:abc123op:public:test@fred x")); + assertThat(builder.ivNonce(null).build(), + equalTo("update:public:test@fred x")); + } + + @Test + public void testUpdateBuilderThrowsExceptionIfMutuallyExclusiveFieldsHaveBeenSet() { + PublicKey key = Keys.publicKeyBuilder() + .sharedBy(createAtSign("fred")) + .name("test") + .build(); + + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> { + VerbBuilders.updateCommandBuilder() + .key(key) + .sharedBy(key.sharedBy()) + .ccd(true) + .value("x") + .build(); + }); + assertThat(ex.getMessage(), containsString("both key and key fields set")); + + } + + @Test + public void testUpdateBuilderGeneratesExpectedOutput() { String command; // self key - builder = new UpdateVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@bob"); - builder.setValue("my Value 123"); - command = builder.build(); // "update:test@bob my Value 123" + command = VerbBuilders.updateCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("@bob")) + .value("my Value 123") + .build(); assertEquals("update:test@bob my Value 123", command); // self key but shared with self - builder = new UpdateVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@bob"); - builder.setSharedWith("@bob"); - builder.setValue("My value 123"); - command = builder.build(); // "update:@alice:test@bob My value 123" + command = VerbBuilders.updateCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("bob")) + .sharedWith(createAtSign("bob")) + .value("My value 123") + .build(); assertEquals("update:@bob:test@bob My value 123", command); // public key - builder = new UpdateVerbBuilder(); - builder.setKeyName("publickey"); - builder.setSharedBy("@bob"); - builder.setIsPublic(true); - builder.setValue("my Value 123"); - command = builder.build(); // "update:public:publickey@bob my Value 123" + command = VerbBuilders.updateCommandBuilder() + .keyName("publickey") + .sharedBy(createAtSign("bob")) + .isPublic(true) + .value("my Value 123") + .build(); assertEquals("update:public:publickey@bob my Value 123", command); // cached public key - builder = new UpdateVerbBuilder(); - builder.setKeyName("publickey"); - builder.setSharedBy("@alice"); - builder.setIsPublic(true); - builder.setIsCached(true); - builder.setValue("my Value 123"); - command = builder.build(); // "update:cached:public:publickey@alice my Value 123" + command = VerbBuilders.updateCommandBuilder() + .keyName("publickey") + .sharedBy(createAtSign("alice")) + .isPublic(true) + .isCached(true) + .value("my Value 123") + .build(); assertEquals("update:cached:public:publickey@alice my Value 123", command); // shared key - builder = new UpdateVerbBuilder(); - builder.setKeyName("sharedkey"); - builder.setSharedBy("@bob"); - builder.setSharedWith("@alice"); - builder.setValue("my Value 123"); - command = builder.build(); // "update:@alice:sharedkey@bob my Value 123" + command = VerbBuilders.updateCommandBuilder() + .keyName("sharedkey") + .sharedBy(createAtSign("@bob")) + .sharedWith(createAtSign("@alice")) + .value("my Value 123") + .build(); assertEquals("update:@alice:sharedkey@bob my Value 123", command); // with shared key - builder = new UpdateVerbBuilder(); - SharedKey sk1 = new KeyBuilders.SharedKeyBuilder(new AtSign("@bob"), new AtSign("@alice")).key("test").build(); - sk1.metadata.isBinary = true; - sk1.metadata.ttl = 1000 * 60 * 10; // 10 minutes - builder.with(sk1, "myBinaryValue123456"); - command = builder.build(); // update:ttl:600000:isBinary:true:isEncrypted:true:@alice:test@bob myBinaryValue123456 + SharedKey sk1 = Keys.sharedKeyBuilder() + .sharedBy(new AtSign("@bob")) + .sharedWith(new AtSign("@alice")) + .name("test") + .ttl(TimeUnit.MINUTES.toMillis(10)) + .isBinary(true) + .build(); + command = VerbBuilders.updateCommandBuilder() + .key(sk1) + .value("myBinaryValue123456") + .build(); assertEquals("update:ttl:600000:isBinary:true:isEncrypted:true:@alice:test@bob myBinaryValue123456", command); // with public key - builder = new UpdateVerbBuilder(); - PublicKey pk1 = new KeyBuilders.PublicKeyBuilder(new AtSign("@bob")).key("test").build(); - pk1.metadata.isCached = true; - builder.with(pk1, "myValue123"); - command = builder.build(); // update:cached:public:test@bob myValue123 - assertEquals("update:isBinary:false:isEncrypted:false:cached:public:test@bob myValue123", command); + PublicKey pk1 = Keys.publicKeyBuilder() + .sharedBy(new AtSign("@bob")) + .name("test") + .isCached(true) + .build(); + command = VerbBuilders.updateCommandBuilder() + .key(pk1) + .value("myValue123") + .build(); + assertEquals("update:isEncrypted:false:cached:public:test@bob myValue123", command); // with self key - builder = new UpdateVerbBuilder(); - SelfKey sk2 = new KeyBuilders.SelfKeyBuilder(new AtSign("@bob")).key("test").build(); - sk2.metadata.ttl = 1000 * 60 * 10; // 10 minutes - builder.with(sk2, "myValue123"); - command = builder.build(); // update:ttl:600000:test@bob myValue123 - assertEquals("update:ttl:600000:isBinary:false:isEncrypted:true:test@bob myValue123", command); + SelfKey sk2 = Keys.selfKeyBuilder() + .sharedBy(new AtSign("@bob")) + .name("test") + .ttl(TimeUnit.MINUTES.toMillis(10)) + .build(); + command = VerbBuilders.updateCommandBuilder() + .key(sk2) + .value("myValue123") + .build(); + assertEquals("update:ttl:600000:isEncrypted:true:test@bob myValue123", command); // with self key (shared with self) - builder = new UpdateVerbBuilder(); - AtSign bob = new AtSign("@bob"); - SelfKey sk3 = new KeyBuilders.SelfKeyBuilder(bob, bob).key("test").build(); - sk3.metadata.ttl = 1000 * 60 * 10; // 10 minutes - builder.with(sk3, "myValue123"); - command = builder.build(); // update:ttl:600000:@bob:test@bob myValue123 - assertEquals("update:ttl:600000:isBinary:false:isEncrypted:true:@bob:test@bob myValue123", command); + AtSign bob = createAtSign("@bob"); + SelfKey sk3 = Keys.selfKeyBuilder() + .sharedBy(bob) + .sharedWith(bob) + .name("test") + .ttl(TimeUnit.MINUTES.toMillis(10)) + .build(); + command = VerbBuilders.updateCommandBuilder() + .key(sk3) + .value("myValue123") + .build(); + assertEquals("update:ttl:600000:isEncrypted:true:@bob:test@bob myValue123", command); // private hidden key // TODO with private hidden key when implemented } @Test - public void testUpdateVerbBuilderForPublicKeyWithNamespace() { - PublicKey key = new KeyBuilders.PublicKeyBuilder(new AtSign("@alice")) - .key("test") + public void testUpdateBuilderGeneratesExpectedOutputForPublicKeyWithNamespace() { + PublicKey key = Keys.publicKeyBuilder() + .sharedBy(new AtSign("@alice")) + .name("test") .namespace("testns") .build(); - UpdateVerbBuilder builder = new UpdateVerbBuilder(); - builder.with(key, "testvalue"); + String command = VerbBuilders.updateCommandBuilder() + .key(key) + .value("testvalue") + .build(); - assertThat(builder.build(), equalTo("update:isBinary:false:isEncrypted:false:public:test.testns@alice testvalue")); + assertThat(command, equalTo("update:isEncrypted:false:public:test.testns@alice testvalue")); } @Test - public void testUpdateVerbBuilderForSelfKeyWithNamespace() { - SelfKey key = new KeyBuilders.SelfKeyBuilder(new AtSign("@alice")) - .key("test") + public void testUpdateBuilderGeneratesExpectedOutputForSelfKeyWithNamespace() { + SelfKey key = Keys.selfKeyBuilder() + .sharedBy(new AtSign("@alice")) + .name("test") .namespace("testns") .build(); - UpdateVerbBuilder builder = new UpdateVerbBuilder(); - builder.with(key, "testvalue"); + String command = VerbBuilders.updateCommandBuilder() + .key(key) + .value("testvalue") + .build(); - assertThat(builder.build(), equalTo("update:isBinary:false:isEncrypted:true:test.testns@alice testvalue")); + assertThat(command, equalTo("update:isEncrypted:true:test.testns@alice testvalue")); } @Test - public void testUpdateVerbBuilderForSharedKeyWithNamespace() { - SharedKey key = new KeyBuilders.SharedKeyBuilder(new AtSign("@alice"), new AtSign("@bob")) - .key("test") + public void testUpdateBuilderGeneratesExpectedOutputForSharedKeyWithNamespace() { + SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(new AtSign("@alice")) + .sharedWith(new AtSign("@bob")) + .name("test") .namespace("testns") .build(); - UpdateVerbBuilder builder = new UpdateVerbBuilder(); - builder.with(key, "testvalue"); + String command = VerbBuilders.updateCommandBuilder() + .key(key) + .value("testvalue") + .build(); - assertThat(builder.build(), equalTo("update:isBinary:false:isEncrypted:true:@bob:test.testns@alice testvalue")); + assertThat(command, equalTo("update:isEncrypted:true:@bob:test.testns@alice testvalue")); } @Test - public void llookupVerbBuilderTest() { - LlookupVerbBuilder builder; + public void testUpdateBuilderGeneratesExpectedOutputForSharedEncryption() { + AtSign sharedBy = createAtSign("sharedBy"); + AtSign sharedWith = createAtSign("sharedWith"); + + String command = VerbBuilders.updateCommandBuilder() + .keyName(AtKeyNames.toSharedByMeKeyName(sharedWith)) + .sharedBy(sharedBy) + .value("XXXX") + .build(); + + assertThat(command, equalTo("update:shared_key.sharedWith@sharedBy XXXX")); + + command = VerbBuilders.updateCommandBuilder() + .keyName(AtKeyNames.SHARED_KEY) + .sharedBy(sharedBy) + .sharedWith(sharedWith) + .ttr(TimeUnit.HOURS.toMillis(24)) + .value("XXXX") + .build(); + + assertThat(command, equalTo("update:ttr:86400000:@sharedWith:shared_key@sharedBy XXXX")); + } + + @Test + public void testUpdateBuilderGeneratesExpectedOutputForPublicEncyrptionKey() { + String command = VerbBuilders.updateCommandBuilder() + .sharedBy(createAtSign("fred")) + .keyName(AtKeyNames.PUBLIC_ENCRYPT) + .isPublic(true) + .value("XXXX") + .build(); + assertThat(command, equalTo("update:public:publickey@fred XXXX")); + } + + @Test + public void testLlookupBuilderGeneratesExpectedOutput() { String command; // Type.NONE self key - builder = new LlookupVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - command = builder.build(); // "llookup:test@alice" + command = VerbBuilders.llookupCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("@alice")) + .build(); assertEquals("llookup:test@alice", command); // Type.METADATA self key - builder = new LlookupVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - builder.setType(LlookupVerbBuilder.Type.METADATA); - command = builder.build(); // "llookup:meta:test@alice" + command = VerbBuilders.llookupCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("@alice")) + .operation(LookupOperation.meta) + .build(); assertEquals("llookup:meta:test@alice", command); // hidden self key, meta - builder = new LlookupVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - builder.setType(LlookupVerbBuilder.Type.METADATA); - builder.setIsHidden(true); - command = builder.build(); // "llookup:meta:_test@alice" + command = VerbBuilders.llookupCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("@alice")) + .operation(LookupOperation.meta) + .isHidden(true) + .build(); assertEquals("llookup:meta:_test@alice", command); // Type.ALL public cached key - builder = new LlookupVerbBuilder(); - builder.setKeyName("publickey"); - builder.setSharedBy("@alice"); - builder.setIsCached(true); - builder.setIsPublic(true); - builder.setType(LlookupVerbBuilder.Type.ALL); - command = builder.build(); // "llookup:cached:public:publickey@alice:all" + command = VerbBuilders.llookupCommandBuilder() + .keyName("publickey") + .sharedBy(createAtSign("@alice")) + .isCached(true) + .isPublic(true) + .operation(LookupOperation.all) + .build(); assertEquals("llookup:all:cached:public:publickey@alice", command); // no key name - assertThrows(IllegalArgumentException.class, () -> { - LlookupVerbBuilder b = new LlookupVerbBuilder(); - b = new LlookupVerbBuilder(); - b.setSharedBy("@alice"); - b.build(); - }); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.llookupCommandBuilder() + .sharedBy(createAtSign("@alice")).build()); + assertThat(ex.getMessage(), containsString("keyName not set")); // no shared by - assertThrows(IllegalArgumentException.class, () -> { - LlookupVerbBuilder b = new LlookupVerbBuilder(); - b = new LlookupVerbBuilder(); - b.setKeyName("test"); - b.build(); - }); + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.llookupCommandBuilder().keyName("test").build()); + assertThat(ex.getMessage(), containsString("sharedBy not set")); - // no key name and no shared by - assertThrows(IllegalArgumentException.class, () -> { - LlookupVerbBuilder b = new LlookupVerbBuilder(); - b.build(); - }); + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.llookupCommandBuilder().keyName("test").rawKey("public:publickey@alice") + .build()); + assertThat(ex.getMessage(), containsString("both rawKey and key fields are set")); // with public key - builder = new LlookupVerbBuilder(); - PublicKey pk = new KeyBuilders.PublicKeyBuilder(new AtSign("@bob")).key("publickey").build(); - builder.with(pk, LlookupVerbBuilder.Type.METADATA); - command = builder.build(); // "llookup:meta:public:publickey@bob" + PublicKey pk = Keys.publicKeyBuilder().sharedBy(new AtSign("@bob")).name("publickey").build(); + command = VerbBuilders.llookupCommandBuilder() + .key(pk) + .operation(LookupOperation.meta) + .build(); + assertEquals("llookup:meta:public:publickey@bob", command); + command = VerbBuilders.llookupCommandBuilder() + .rawKey("public:publickey@bob") + .operation(LookupOperation.meta) + .build(); assertEquals("llookup:meta:public:publickey@bob", command); // with shared key - builder = new LlookupVerbBuilder(); - SharedKey sk = new KeyBuilders.SharedKeyBuilder(new AtSign("@bob"), new AtSign("@alice")).key("sharedkey").build(); - builder.with(sk, LlookupVerbBuilder.Type.NONE); - command = builder.build(); // "llookup:@alice:sharedkey@bob" + SharedKey sk = Keys.sharedKeyBuilder() + .sharedBy(new AtSign("@bob")) + .sharedWith(new AtSign("@alice")) + .name("sharedkey") + .build(); + command = VerbBuilders.llookupCommandBuilder() + .key(sk) + .operation(LookupOperation.none) + .build(); assertEquals("llookup:@alice:sharedkey@bob", command); // with self key - builder = new LlookupVerbBuilder(); - SelfKey selfKey1 = new KeyBuilders.SelfKeyBuilder(new AtSign("@bob")).key("test").build(); - builder.with(selfKey1, LlookupVerbBuilder.Type.ALL); - command = builder.build(); // "llookup:all:test@bob" + SelfKey selfKey1 = Keys.selfKeyBuilder().sharedBy(new AtSign("@bob")).name("test").build(); + command = VerbBuilders.llookupCommandBuilder() + .key(selfKey1) + .operation(LookupOperation.all) + .build(); // "llookup:all:test@bob" assertEquals("llookup:all:test@bob", command); // with self key (shared with self) - builder = new LlookupVerbBuilder(); AtSign as = new AtSign("@bob"); - SelfKey selfKey2 = new KeyBuilders.SelfKeyBuilder(as, as).key("test").build(); - builder.with(selfKey2, LlookupVerbBuilder.Type.ALL); - command = builder.build(); // "llookup:all:@bob:test@bob" + SelfKey selfKey2 = Keys.selfKeyBuilder().sharedBy(as).sharedWith(as).name("test").build(); + command = VerbBuilders.llookupCommandBuilder() + .key(selfKey2) + .operation(LookupOperation.all) + .build(); assertEquals("llookup:all:@bob:test@bob", command); - // with cached public key - builder = new LlookupVerbBuilder(); - PublicKey pk2 = new KeyBuilders.PublicKeyBuilder(new AtSign("@bob")).key("publickey").build(); - pk2.metadata.isCached = true; - builder.with(pk2, LlookupVerbBuilder.Type.ALL); - command = builder.build(); // "llookup:all:cached:public:publickey@bob" + PublicKey pk2 = Keys.publicKeyBuilder() + .sharedBy(new AtSign("@bob")) + .name("publickey") + .isCached(true) + .build(); + command = VerbBuilders.llookupCommandBuilder() + .key(pk2) + .operation(LookupOperation.all) + .build(); assertEquals("llookup:all:cached:public:publickey@bob", command); // with cached shared key - builder = new LlookupVerbBuilder(); - SharedKey sk2 = new KeyBuilders.SharedKeyBuilder(new AtSign("@bob"), new AtSign("@alice")).key("sharedkey").build(); - sk2.metadata.isCached = true; - builder.with(sk2, LlookupVerbBuilder.Type.NONE); - command = builder.build(); // "llookup:cached:@alice:sharedkey@bob" + SharedKey sk2 = Keys.sharedKeyBuilder() + .sharedBy(new AtSign("@bob")) + .sharedWith(new AtSign("@alice")) + .name("sharedkey") + .isCached(true) + .build(); + command = VerbBuilders.llookupCommandBuilder() + .key(sk2) + .operation(LookupOperation.none) + .build(); assertEquals("llookup:cached:@alice:sharedkey@bob", command); // with private hidden key @@ -294,492 +553,798 @@ public void llookupVerbBuilderTest() { } @Test - public void testLlookupVerbBuilderFoPublicKeyWithNamespace() { - PublicKey key = new KeyBuilders.PublicKeyBuilder(new AtSign("@alice")) - .key("test") + public void testLlookupBuilderGeneratesExpectedOutputForPublicKeyWithNamespace() { + PublicKey key = Keys.publicKeyBuilder().sharedBy(new AtSign("@alice")) + .name("test") .namespace("testns") .build(); - LlookupVerbBuilder builder = new LlookupVerbBuilder(); - builder.with(key, LlookupVerbBuilder.Type.METADATA); - - assertThat(builder.build(), equalTo("llookup:meta:public:test.testns@alice")); + String command = VerbBuilders.llookupCommandBuilder() + .key(key) + .operation(LookupOperation.meta) + .build(); + assertThat(command, equalTo("llookup:meta:public:test.testns@alice")); } @Test - public void testLlookupVerbBuilderForSelfKeyWithNamespace() { - SelfKey key = new KeyBuilders.SelfKeyBuilder(new AtSign("@alice")) - .key("test") + public void testLlookupBuilderGeneratesExpectedOutputForSelfKeyWithNamespace() { + SelfKey key = Keys.selfKeyBuilder() + .sharedBy(new AtSign("@alice")) + .name("test") .namespace("testns") .build(); - LlookupVerbBuilder builder = new LlookupVerbBuilder(); - builder.with(key, LlookupVerbBuilder.Type.METADATA); - - assertThat(builder.build(), equalTo("llookup:meta:test.testns@alice")); + String command = VerbBuilders.llookupCommandBuilder() + .key(key) + .operation(LookupOperation.meta) + .build(); + assertThat(command, equalTo("llookup:meta:test.testns@alice")); } @Test - public void testLlookupVerbBuilderForSharedKeyWithNamespace() { - SharedKey key = new KeyBuilders.SharedKeyBuilder(new AtSign("@alice"), new AtSign("@bob")) - .key("test") + public void testLlookupBuilderGeneratesExpectedOutputForSharedKeyWithNamespace() { + SharedKey key = Keys.sharedKeyBuilder().sharedBy(new AtSign("@alice")) + .sharedWith(new AtSign("@bob")) + .name("test") .namespace("testns") .build(); - LlookupVerbBuilder builder = new LlookupVerbBuilder(); - builder.with(key, LlookupVerbBuilder.Type.METADATA); - - assertThat(builder.build(), equalTo("llookup:meta:@bob:test.testns@alice")); + String command = VerbBuilders.llookupCommandBuilder() + .key(key) + .operation(LookupOperation.meta) + .build(); + assertThat(command, equalTo("llookup:meta:@bob:test.testns@alice")); } @Test public void lookupVerbBuilderTest() { - LookupVerbBuilder builder; String command; // Type.NONE - builder = new LookupVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - command = builder.build(); // "lookup:test@alice" + command = VerbBuilders.lookupCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("@alice")) + .build(); + assertEquals("lookup:test@alice", command); + command = VerbBuilders.lookupCommandBuilder() + .rawKey("test@alice") + .build(); assertEquals("lookup:test@alice", command); // Type.METADATA - builder = new LookupVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - builder.setType(LookupVerbBuilder.Type.METADATA); - command = builder.build(); // "lookup:meta:test@alice" + command = VerbBuilders.lookupCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("@alice")) + .operation(LookupOperation.meta) + .build(); assertEquals("lookup:meta:test@alice", command); // Type.ALL - builder = new LookupVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - builder.setType(LookupVerbBuilder.Type.ALL); - command = builder.build(); // "lookup:test@alice" + command = VerbBuilders.lookupCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("@alice")) + .operation(LookupOperation.all) + .build(); // "lookup:test@alice" assertEquals("lookup:all:test@alice", command); // no key name - assertThrows(IllegalArgumentException.class, () -> { - LookupVerbBuilder b = new LookupVerbBuilder(); - b = new LookupVerbBuilder(); - b.setSharedBy("@alice"); - b.build(); - }); - - // no sharedWith - assertThrows(IllegalArgumentException.class, () -> { - LookupVerbBuilder b = new LookupVerbBuilder(); - b = new LookupVerbBuilder(); - b.setKeyName("test"); - b.build(); - }); + IllegalArgumentException ex = + assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.lookupCommandBuilder().sharedBy(createAtSign("@alice")).build()); + assertThat(ex.getMessage(), containsString("keyName not set")); - // no key name and no shared with - assertThrows(IllegalArgumentException.class, () -> { - LookupVerbBuilder b = new LookupVerbBuilder(); - b.build(); - }); + // no sharedBy + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.lookupCommandBuilder().keyName("test").build()); + assertThat(ex.getMessage(), containsString("sharedBy not set")); // with shared key - builder = new LookupVerbBuilder(); - SharedKey sk = - new KeyBuilders.SharedKeyBuilder(new AtSign("@sharedby"), new AtSign("@sharedwith")).key("test").build(); - builder.with(sk, LookupVerbBuilder.Type.METADATA); - command = builder.build(); // "lookup:meta:test@sharedby" + SharedKey sk = Keys.sharedKeyBuilder().sharedBy(new AtSign("@sharedby")) + .sharedWith(new AtSign("@sharedwith")) + .name("test") + .build(); + command = VerbBuilders.lookupCommandBuilder() + .key(sk) + .operation(LookupOperation.meta) + .build(); assertEquals("lookup:meta:test@sharedby", command); } @Test public void testLookupVerbBuilderForSharedKeyWithNamespace() { - SharedKey key = new KeyBuilders.SharedKeyBuilder(new AtSign("@alice"), new AtSign("@bob")) - .key("test") + SharedKey key = Keys.sharedKeyBuilder().sharedBy(new AtSign("@alice")) + .sharedWith(new AtSign("@bob")) + .name("test") .namespace("testns") .build(); - LookupVerbBuilder builder = new LookupVerbBuilder(); - builder.with(key, LookupVerbBuilder.Type.METADATA); - - assertThat(builder.build(), equalTo("lookup:meta:test.testns@alice")); + String command = VerbBuilders.lookupCommandBuilder() + .key(key) + .operation(LookupOperation.meta) + .build(); + assertThat(command, equalTo("lookup:meta:test.testns@alice")); } @Test public void plookupVerbBuilderTest() { - PlookupVerbBuilder builder; String command; // Type.NONE - builder = new PlookupVerbBuilder(); - builder.setKeyName("publickey"); - builder.setSharedBy("@alice"); - command = builder.build(); // "plookup:publickey@alice" + command = VerbBuilders.plookupCommandBuilder() + .keyName("publickey") + .sharedBy(createAtSign("@alice")) + .build(); // "plookup:publickey@alice" + assertEquals("plookup:publickey@alice", command); + command = VerbBuilders.plookupCommandBuilder() + .rawKey("publickey@alice") + .build(); // "plookup:publickey@alice" assertEquals("plookup:publickey@alice", command); // Type.METADATA - builder = new PlookupVerbBuilder(); - builder.setKeyName("publickey"); - builder.setSharedBy("@alice"); - builder.setType(PlookupVerbBuilder.Type.METADATA); - command = builder.build(); // "plookup:meta:publickey@alice" + command = VerbBuilders.plookupCommandBuilder() + .keyName("publickey") + .sharedBy(createAtSign("@alice")) + .operation(LookupOperation.meta) + .build(); assertEquals("plookup:meta:publickey@alice", command); // Type.ALL - builder = new PlookupVerbBuilder(); - builder.setKeyName("publickey"); - builder.setSharedBy("@alice"); - builder.setType(PlookupVerbBuilder.Type.ALL); - command = builder.build(); // "plookup:all:publickey@alice" + command = VerbBuilders.plookupCommandBuilder() + .keyName("publickey") + .sharedBy(createAtSign("@alice")) + .operation(LookupOperation.all) + .build(); assertEquals("plookup:all:publickey@alice", command); // no key - assertThrows(IllegalArgumentException.class, () -> { - PlookupVerbBuilder b = new PlookupVerbBuilder(); - b.setSharedBy("@alice"); - b.setType(Type.ALL); - b.build(); - }); - + IllegalArgumentException ex = + assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.plookupCommandBuilder().sharedBy(createAtSign("@alice")) + .operation(LookupOperation.all) + .build()); + assertThat(ex.getMessage(), containsString("keyName not set")); // no shared by - assertThrows(IllegalArgumentException.class, () -> { - PlookupVerbBuilder b = new PlookupVerbBuilder(); - b.setKeyName("publickey"); - b.setType(Type.ALL); - b.build(); - }); - - // no key and no shared by - assertThrows(IllegalArgumentException.class, () -> { - PlookupVerbBuilder b = new PlookupVerbBuilder(); - b.setType(Type.ALL); - b.build(); - }); + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.plookupCommandBuilder().keyName("publickey").operation(LookupOperation.all) + .build()); + assertThat(ex.getMessage(), containsString("sharedBy not set")); // with - builder = new PlookupVerbBuilder(); - PublicKey pk = new KeyBuilders.PublicKeyBuilder(new AtSign("@bob")).key("publickey").build(); - builder.with(pk, Type.ALL); - command = builder.build(); // "plookup:all:publickey@bob" + PublicKey pk = Keys.publicKeyBuilder().sharedBy(new AtSign("@bob")).name("publickey").build(); + command = VerbBuilders.plookupCommandBuilder() + .key(pk) + .operation(LookupOperation.all) + .build(); assertEquals("plookup:all:publickey@bob", command); // bypasscache true - builder = new PlookupVerbBuilder(); - builder.setKeyName("publickey"); - builder.setSharedBy("@alice"); - builder.setBypassCache(true); - builder.setType(Type.ALL); - command = builder.build(); // "plookup:bypassCache:true:all:publickey@alice" + command = VerbBuilders.plookupCommandBuilder() + .keyName("publickey") + .sharedBy(createAtSign("@alice")) + .bypassCache(true) + .operation(LookupOperation.all) + .build(); assertEquals("plookup:bypassCache:true:all:publickey@alice", command); } @Test public void testPlookupVerbBuilderForPublicKeyWithNamespace() { - PublicKey key = new KeyBuilders.PublicKeyBuilder(new AtSign("@alice")) - .key("test") + PublicKey key = Keys.publicKeyBuilder() + .sharedBy(new AtSign("@alice")) + .name("test") .namespace("testns") .build(); - PlookupVerbBuilder builder = new PlookupVerbBuilder(); - builder.with(key, PlookupVerbBuilder.Type.METADATA); - - assertThat(builder.build(), equalTo("plookup:meta:test.testns@alice")); + String command = VerbBuilders.plookupCommandBuilder() + .key(key) + .operation(LookupOperation.meta) + .build(); + assertThat(command, equalTo("plookup:meta:test.testns@alice")); } @Test public void deleteVerbBuilderTest() { - DeleteVerbBuilder builder; String command; // delete a public key - builder = new DeleteVerbBuilder(); - builder.setIsPublic(true); - builder.setKeyName("publickey"); - builder.setSharedBy("@alice"); - command = builder.build(); + command = VerbBuilders.deleteCommandBuilder() + .isPublic(true) + .keyName("publickey") + .sharedBy(createAtSign("@alice")) + .build(); + assertEquals("delete:public:publickey@alice", command); + command = VerbBuilders.deleteCommandBuilder() + .rawKey("public:publickey@alice") + .build(); assertEquals("delete:public:publickey@alice", command); // delete a cached public key - builder = new DeleteVerbBuilder(); - builder.setIsCached(true); - builder.setIsPublic(true); - builder.setKeyName("publickey"); - builder.setSharedBy("@bob"); - command = builder.build(); + command = VerbBuilders.deleteCommandBuilder() + .isCached(true) + .isPublic(true) + .keyName("publickey") + .sharedBy(createAtSign("@bob")) + .build(); assertEquals("delete:cached:public:publickey@bob", command); // delete a self key - builder = new DeleteVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - command = builder.build(); + command = VerbBuilders.deleteCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("@alice")) + .build(); assertEquals("delete:test@alice", command); // delete a hidden self key - builder = new DeleteVerbBuilder(); - builder.setIsHidden(true); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - command = builder.build(); + command = VerbBuilders.deleteCommandBuilder() + .isHidden(true) + .keyName("test") + .sharedBy(createAtSign("@alice")) + .build(); assertEquals("delete:_test@alice", command); // delete a shared key - builder = new DeleteVerbBuilder(); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - builder.setSharedWith("@bob"); - command = builder.build(); + command = VerbBuilders.deleteCommandBuilder() + .keyName("test") + .sharedBy(createAtSign("@alice")) + .sharedWith(createAtSign("@bob")) + .build(); assertEquals("delete:@bob:test@alice", command); // delete a cached shared key - builder = new DeleteVerbBuilder(); - builder.setIsCached(true); - builder.setKeyName("test"); - builder.setSharedBy("@alice"); - builder.setSharedWith("@bob"); - command = builder.build(); + command = VerbBuilders.deleteCommandBuilder() + .isCached(true) + .keyName("test") + .sharedBy(createAtSign("@alice")) + .sharedWith(createAtSign("@bob")) + .build(); assertEquals("delete:cached:@bob:test@alice", command); // missing key name - assertThrows(IllegalArgumentException.class, () -> { - DeleteVerbBuilder b = new DeleteVerbBuilder(); - b.setSharedBy("@alice"); - b.setSharedWith("@bob"); - b.build(); - }); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.deleteCommandBuilder() + .sharedBy(createAtSign("@alice")) + .sharedWith(createAtSign("@bob")) + .build()); + assertThat(ex.getMessage(), containsString("keyName not set")); // missing shared by - assertThrows(IllegalArgumentException.class, () -> { - DeleteVerbBuilder b = new DeleteVerbBuilder(); - b.setKeyName("test"); - b.build(); - }); - - // missing key name and shared by - assertThrows(IllegalArgumentException.class, () -> { - DeleteVerbBuilder b = new DeleteVerbBuilder(); - b.build(); - }); + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.deleteCommandBuilder().keyName("test").build()); + assertThat(ex.getMessage(), containsString("sharedBy not set")); // with self key - builder = new DeleteVerbBuilder(); - SelfKey selfKey = new KeyBuilders.SelfKeyBuilder(new AtSign("@alice")).key("test").build(); - builder.with(selfKey); - command = builder.build(); + SelfKey selfKey = Keys.selfKeyBuilder().sharedBy(new AtSign("@alice")).name("test").build(); + command = VerbBuilders.deleteCommandBuilder() + .key(selfKey) + .build(); assertEquals("delete:test@alice", command); // with public key - builder = new DeleteVerbBuilder(); - PublicKey pk = new KeyBuilders.PublicKeyBuilder(new AtSign("@bob")).key("publickey").build(); - builder.with(pk); - command = builder.build(); + PublicKey pk = Keys.publicKeyBuilder().sharedBy(new AtSign("@bob")).name("publickey").build(); + command = VerbBuilders.deleteCommandBuilder() + .key(pk) + .build(); // with shared key - builder = new DeleteVerbBuilder(); - SharedKey sk = new KeyBuilders.SharedKeyBuilder(new AtSign("@alice"), new AtSign("@bob")).key("test").build(); - builder.with(sk); - command = builder.build(); + SharedKey sk = Keys.sharedKeyBuilder() + .sharedBy(new AtSign("@alice")) + .sharedWith(new AtSign("@bob")) + .name("test") + .build(); + command = VerbBuilders.deleteCommandBuilder() + .key(sk) + .build(); assertEquals("delete:@bob:test@alice", command); - } @Test public void testDeleteVerbBuilderForPublicKeyWithNamespace() { - PublicKey key = new KeyBuilders.PublicKeyBuilder(new AtSign("@alice")) - .key("test") + PublicKey key = Keys.publicKeyBuilder() + .sharedBy(new AtSign("@alice")) + .name("test") .namespace("testns") .build(); - DeleteVerbBuilder builder = new DeleteVerbBuilder(); - builder.with(key); - - assertThat(builder.build(), equalTo("delete:public:test.testns@alice")); + String command = VerbBuilders.deleteCommandBuilder() + .key(key) + .build(); + assertThat(command, equalTo("delete:public:test.testns@alice")); } @Test public void testDeleteVerbBuilderForSelfKeyWithNamespace() { - SelfKey key = new KeyBuilders.SelfKeyBuilder(new AtSign("@alice")) - .key("test") + SelfKey key = Keys.selfKeyBuilder() + .sharedBy(new AtSign("@alice")) + .name("test") .namespace("testns") .build(); - DeleteVerbBuilder builder = new DeleteVerbBuilder(); - builder.with(key); - - assertThat(builder.build(), equalTo("delete:test.testns@alice")); + String command = VerbBuilders.deleteCommandBuilder() + .key(key) + .build(); + assertThat(command, equalTo("delete:test.testns@alice")); } @Test public void testDeleteVerbBuilderForSharedKeyWithNamespace() { - SharedKey key = new KeyBuilders.SharedKeyBuilder(new AtSign("@alice"), new AtSign("@bob")) - .key("test") + SharedKey key = Keys.sharedKeyBuilder() + .sharedBy(new AtSign("@alice")).sharedWith(new AtSign("@bob")) + .name("test") .namespace("testns") .build(); - DeleteVerbBuilder builder = new DeleteVerbBuilder(); - builder.with(key); - - assertThat(builder.build(), equalTo("delete:@bob:test.testns@alice")); + String command = VerbBuilders.deleteCommandBuilder() + .key(key) + .build(); + assertThat(command, equalTo("delete:@bob:test.testns@alice")); } @Test public void scanVerbBuilderTest() { // Test not setting any parameters - ScanVerbBuilder scanVerbBuilder = new ScanVerbBuilder(); - String command = scanVerbBuilder.build(); + String command = VerbBuilders.scanCommandBuilder().build(); assertEquals("scan", command); // Test setting just regex - scanVerbBuilder = new ScanVerbBuilder(); - scanVerbBuilder.setRegex("*.public"); - command = scanVerbBuilder.build(); + command = VerbBuilders.scanCommandBuilder().regex("*.public") + .build(); assertEquals("scan *.public", command); // Test setting just fromAtSign - scanVerbBuilder = new ScanVerbBuilder(); - scanVerbBuilder.setFromAtSign("@other"); - command = scanVerbBuilder.build(); + command = VerbBuilders.scanCommandBuilder() + .fromAtSign(createAtSign("@other")) + .build(); assertEquals("scan:@other", command); // Test seting just showHidden - scanVerbBuilder = new ScanVerbBuilder(); - scanVerbBuilder.setShowHidden(true); - command = scanVerbBuilder.build(); + command = VerbBuilders.scanCommandBuilder() + .showHidden(true) + .build(); assertEquals("scan:showHidden:true", command); // Test setting regex & fromAtSign - scanVerbBuilder = new ScanVerbBuilder(); - scanVerbBuilder.setRegex("*.public"); - scanVerbBuilder.setFromAtSign("@other"); - command = scanVerbBuilder.build(); + command = VerbBuilders.scanCommandBuilder() + .regex("*.public") + .fromAtSign(createAtSign("@other")) + .build(); assertEquals("scan:@other *.public", command); // Test setting regex & showHidden - scanVerbBuilder = new ScanVerbBuilder(); - scanVerbBuilder.setRegex("*.public"); - scanVerbBuilder.setShowHidden(true); - command = scanVerbBuilder.build(); + command = VerbBuilders.scanCommandBuilder() + .regex("*.public") + .showHidden(true) + .build(); assertEquals("scan:showHidden:true *.public", command); // Test setting fromAtSign & showHidden - scanVerbBuilder = new ScanVerbBuilder(); - scanVerbBuilder.setFromAtSign("@other"); - scanVerbBuilder.setShowHidden(true); - command = scanVerbBuilder.build(); + command = VerbBuilders.scanCommandBuilder() + .fromAtSign(createAtSign("@other")) + .showHidden(true) + .build(); assertEquals("scan:showHidden:true:@other", command); // Test setting regex & fromAtSign & showHidden - scanVerbBuilder = new ScanVerbBuilder(); - scanVerbBuilder.setRegex("*.public"); - scanVerbBuilder.setFromAtSign("@other"); - scanVerbBuilder.setShowHidden(true); - command = scanVerbBuilder.build(); + command = VerbBuilders.scanCommandBuilder() + .regex("*.public") + .fromAtSign(createAtSign("@other")) + .showHidden(true) + .build(); assertEquals("scan:showHidden:true:@other *.public", command); } @Test - public void notifyTextBuilderTest() { + public void testNotifyTextBuilderGeneratesTheExpectedOutput() { // Test not setting any parameters - final NotifyTextVerbBuilder builderWithNoFieldsSet = new NotifyTextVerbBuilder(); - assertThrows(IllegalArgumentException.class, builderWithNoFieldsSet::build, - "Recipient @sign and text are mandatory. Expecting a IllegalArgumentException being thrown."); + IllegalArgumentException ex = + assertThrows(IllegalArgumentException.class, () -> VerbBuilders.notifyTextCommandBuilder().build()); + assertThat(ex.getMessage(), containsString("recipient not set")); // Test not setting the text - final NotifyTextVerbBuilder builderWithTextFieldUnset = new NotifyTextVerbBuilder(); - builderWithTextFieldUnset.setRecipientAtSign("@test"); - assertThrows(IllegalArgumentException.class, builderWithTextFieldUnset::build, - "Text is mandatory. Expecting a IllegalArgumentException being thrown."); - - NotifyTextVerbBuilder notifyTextBuilder = new NotifyTextVerbBuilder(); - notifyTextBuilder.setText("Hi"); - notifyTextBuilder.setRecipientAtSign("@test"); - String expectedResult = "notify:messageType:text:@test:Hi"; - assertEquals(expectedResult, notifyTextBuilder.build()); - - // test not setting an '@' sign to the recipients at sign and expect it to be - // appended properly - notifyTextBuilder = new NotifyTextVerbBuilder(); - notifyTextBuilder.setText("Hello"); - notifyTextBuilder.setRecipientAtSign("test"); - expectedResult = "notify:messageType:text:@test:Hello"; - assertEquals(expectedResult, notifyTextBuilder.build()); + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.notifyTextCommandBuilder().recipient(createAtSign("@somebody")).build()); + assertThat(ex.getMessage(), containsString("text not set")); + String command = VerbBuilders.notifyTextCommandBuilder() + .recipient(createAtSign("@test")) + .text("Hi") + .build(); + assertEquals("notify:messageType:text:@test:Hi", command); } @Test public void notifyKeyChangeBuilderTest() { // Test not setting any parameters - final NotifyKeyChangeBuilder builderWithNoArgsSet = new NotifyKeyChangeBuilder(); - assertThrows(IllegalArgumentException.class, builderWithNoArgsSet::build, - "Mandatory fields are not set. Expecting a IllegalArgumentException being thrown."); - - // Test not setting the key - final NotifyKeyChangeBuilder builderWithNoKeySet = new NotifyKeyChangeBuilder(); - builderWithNoKeySet.setOperation("update"); - builderWithNoKeySet.setSenderAtSign("@sender"); - builderWithNoKeySet.setRecipientAtSign("@recipient"); - assertThrows(IllegalArgumentException.class, builderWithNoKeySet::build, - "Key is mandatory. Expecting a IllegalArgumentException being thrown."); + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.notifyKeyChangeCommandBuilder() + .operation(NotifyOperation.update) + .sender(createAtSign("sender")) + .recipient(createAtSign("recipient")) + .build()); + assertThat(ex.getMessage(), containsString("key not set")); + + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.notifyKeyChangeCommandBuilder().key("key").build()); + assertThat(ex.getMessage(), containsString("operation not set")); // Test setting the value when ttr has been set - final NotifyKeyChangeBuilder builderWithTrrSetButNoValue = new NotifyKeyChangeBuilder(); - builderWithTrrSetButNoValue.setOperation("update"); - builderWithTrrSetButNoValue.setSenderAtSign("@sender"); - builderWithTrrSetButNoValue.setRecipientAtSign("@recipient"); - builderWithTrrSetButNoValue.setKey("phone"); - builderWithTrrSetButNoValue.setTtr(10000); - assertThrows(IllegalArgumentException.class, builderWithTrrSetButNoValue::build, - "Value is mandatory if ttr has been set. Expecting a IllegalArgumentException being thrown."); + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.notifyKeyChangeCommandBuilder() + .operation(NotifyOperation.update) + .sender(createAtSign("sender")) + .recipient(createAtSign("recipient")) + .key("phone") + .ttr(10000L) + .build()); + assertThat(ex.getMessage(), containsString("value not set (mandatory when ttr is set)")); // Test setting invalid ttr - final NotifyKeyChangeBuilder builderWithNegativeTrrSetButNoValue = new NotifyKeyChangeBuilder(); - builderWithNegativeTrrSetButNoValue.setOperation("update"); - builderWithNegativeTrrSetButNoValue.setSenderAtSign("@sender"); - builderWithNegativeTrrSetButNoValue.setRecipientAtSign("@recipient"); - builderWithNegativeTrrSetButNoValue.setKey("phone"); - builderWithNegativeTrrSetButNoValue.setTtr(-100); - assertThrows(IllegalArgumentException.class, builderWithNegativeTrrSetButNoValue::build, - "Value is mandatory if ttr has been set. Expecting a IllegalArgumentException being thrown."); + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.notifyKeyChangeCommandBuilder() + .operation(NotifyOperation.update) + .sender(createAtSign("sender")) + .recipient(createAtSign("recipient")) + .key("phone") + .ttr(-100L) + .build()); + assertThat(ex.getMessage(), containsString("ttr < -1")); // test command - NotifyKeyChangeBuilder notifyKeyChangeBuilder = new NotifyKeyChangeBuilder(); - notifyKeyChangeBuilder.setOperation("update"); - notifyKeyChangeBuilder.setSenderAtSign("@sender"); - notifyKeyChangeBuilder.setRecipientAtSign("@recipient"); - notifyKeyChangeBuilder.setKey("phone"); - // Expect build to throw Illegal argument exception for not setting the text - String command = notifyKeyChangeBuilder.build(); - String expectedResult = "notify:update:messageType:key:@recipient:phone@sender"; - assertEquals(expectedResult, command); + String command = VerbBuilders.notifyKeyChangeCommandBuilder() + .operation(NotifyOperation.update) + .sender(createAtSign("sender")) + .recipient(createAtSign("recipient")) + .key("phone") + .build(); + assertEquals("notify:update:messageType:key:@recipient:phone@sender", command); // test command with a fully formed key - notifyKeyChangeBuilder = new NotifyKeyChangeBuilder(); - notifyKeyChangeBuilder.setOperation("update"); - notifyKeyChangeBuilder.setKey("@recipient:phone@sender"); - // Expect build to throw Illegal argument exception for not setting the text - command = notifyKeyChangeBuilder.build(); - expectedResult = "notify:update:messageType:key:@recipient:phone@sender"; - assertEquals(expectedResult, command); + command = VerbBuilders.notifyKeyChangeCommandBuilder() + .operation(NotifyOperation.update) + .key("@recipient:phone@sender") + .build(); + assertEquals("notify:update:messageType:key:@recipient:phone@sender", command); // test command when ttr and value are present - notifyKeyChangeBuilder = new NotifyKeyChangeBuilder(); - notifyKeyChangeBuilder.setOperation("update"); - notifyKeyChangeBuilder.setKey("@recipient:phone@sender"); - notifyKeyChangeBuilder.setTtr(1000); - notifyKeyChangeBuilder.setValue("cache_me"); - // Expect build to throw Illegal argument exception for not setting the text - command = notifyKeyChangeBuilder.build(); - expectedResult = "notify:update:messageType:key:ttr:1000:@recipient:phone@sender:cache_me"; - assertEquals(expectedResult, command); - + command = VerbBuilders.notifyKeyChangeCommandBuilder() + .operation(NotifyOperation.update) + .key("@recipient:phone@sender") + .ttr(1000L) + .value("cache_me") + .build(); + assertEquals("notify:update:messageType:key:ttr:1000:@recipient:phone@sender:cache_me", command); } @Test public void notificationStatusVerbBuilderTest() { // Test not setting any parameters - final NotificationStatusVerbBuilder builderWithNoFieldsSet = new NotificationStatusVerbBuilder(); - assertThrows(IllegalArgumentException.class, builderWithNoFieldsSet::build, - "Mandatory fields are not set. Expecting a IllegalArgumentException being thrown."); + IllegalArgumentException ex = + assertThrows(IllegalArgumentException.class, () -> VerbBuilders.notifyStatusCommandBuilder().build()); + assertThat(ex.getMessage(), containsString("notificationId not set")); + + String command = VerbBuilders.notifyStatusCommandBuilder() + .notificationId("n1234").build(); + assertEquals("notify:status:n1234", command); + } + + @Test + void testEnrollThrowExceptionIfOperationIsNotSet() { + IllegalArgumentException ex = + assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder().build()); + assertThat(ex.getMessage(), containsString("operation not set")); + } + + @Test + void testEnrollListReturnsExpectedCommand() { + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.list) + .build(); + + assertThat(result, is("enroll:list")); + } + + @Test + void testEnrollListWithStatusProducesCommandWithStatusFilter() { + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.list) + .status("pending") + .build(); + + assertThat(result, is("enroll:list{\"enrollmentStatusFilter\":[\"pending\"]}")); + } + + @Test + void testEnrollApproveThrowsIfFieldsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.approve) + .encryptPrivateKey("privKey") + .encryptPrivateKeyIv("privKeyIv") + .selfEncryptKey("selfKey") + .selfEncryptKeyIv("selfKeyIv") + .build()); + assertThat(ex.getMessage(), containsString("enrollmentId not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.approve) + .enrollmentId(createEnrollmentId("abc123")) + .encryptPrivateKeyIv("privKeyIv") + .selfEncryptKey("selfKey") + .selfEncryptKeyIv("selfKeyIv") + .build()); + assertThat(ex.getMessage(), containsString("encryptPrivateKey not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.approve) + .enrollmentId(createEnrollmentId("abc123")) + .encryptPrivateKey("privKey") + .selfEncryptKey("selfKey") + .selfEncryptKeyIv("selfKeyIv") + .build()); + + assertThat(ex.getMessage(), containsString("encryptPrivateKeyIv not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.approve) + .enrollmentId(createEnrollmentId("abc123")) + .encryptPrivateKey("privKey") + .encryptPrivateKeyIv("privKeyIv") + .selfEncryptKeyIv("selfKeyIv") + .build()); + + assertThat(ex.getMessage(), containsString("selfEncryptKey not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.approve) + .enrollmentId(createEnrollmentId("abc123")) + .encryptPrivateKey("privKey") + .encryptPrivateKeyIv("privKeyIv") + .selfEncryptKey("selfKey") + .build()); + + assertThat(ex.getMessage(), containsString("selfEncryptKeyIv not set")); + + } + + @Test + void testEnrollApproveWithAllRequiredParamsReturnsExpectedCommand() { + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.approve) + .enrollmentId(createEnrollmentId("abc123")) + .encryptPrivateKey("privKey") + .encryptPrivateKeyIv("privKeyIv") + .selfEncryptKey("selfKey") + .selfEncryptKeyIv("selfKeyIv") + .build(); + + assertThat(result, is("enroll:approve{" + + "\"enrollmentId\":\"abc123\"," + + "\"encryptedDefaultEncryptionPrivateKey\":\"privKey\"," + + "\"encPrivateKeyIV\":\"privKeyIv\"," + + "\"encryptedDefaultSelfEncryptionKey\":\"selfKey\"," + + "\"selfEncKeyIV\":\"selfKeyIv\"" + + "}")); + } + + @Test + void testEnrollFetchThrowsExpectedExceptionWhenFieldsAreNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.fetch) + .build()); + + assertThat(ex.getMessage(), containsString("enrollmentId not set")); + } + + @Test + void testEnrollFetchWithEnrollmentIdReturnsExpectedCommand() { + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.fetch) + .enrollmentId(createEnrollmentId("abc123")) + .build(); - final NotificationStatusVerbBuilder notificationStatusVerbBuilder = new NotificationStatusVerbBuilder(); - notificationStatusVerbBuilder.setNotificationId("n1234"); - String expectedResult = "notify:status:n1234"; - assertEquals(expectedResult, notificationStatusVerbBuilder.build()); + assertThat(result, is("enroll:fetch{\"enrollmentId\":\"abc123\"}")); + } + + @Test + void testEnrollDenyThrowExceptionIfEnrollmentIdIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.deny) + .build()); + + assertThat(ex.getMessage(), containsString("enrollmentId not set")); + } + + @Test + void testEnrollDenyWithEnrollmentIdProducesCorrectCommand() { + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.deny) + .enrollmentId(createEnrollmentId("abc123")) + .build(); + + assertThat(result, is("enroll:deny{\"enrollmentId\":\"abc123\"}")); + } + + @Test + void testEnrollRevokeThrowExceptionIfEnrollmentIdIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.revoke) + .build()); + + assertThat(ex.getMessage(), containsString("enrollmentId not set")); + } + + @Test + void testEnrollRevokeWithEnrollmentIdProducesCorrectCommand() { + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.revoke) + .enrollmentId(createEnrollmentId("abc123")) + .build(); + + assertThat(result, is("enroll:revoke{\"enrollmentId\":\"abc123\"}")); + } + + @Test + void testEnrollUnrevokeThrowExceptionIfEnrollmentIdIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.unrevoke) + .build()); + + assertThat(ex.getMessage(), containsString("enrollmentId not set")); + } + + @Test + void testEnrollUnrevokeWithEnrollmentIdProducesCorrectCommand() { + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.unrevoke) + .enrollmentId(createEnrollmentId("abc123")) + .build(); + + assertThat(result, is("enroll:unrevoke{\"enrollmentId\":\"abc123\"}")); + } + + @Test + void testEnrollDeleteThrowExceptionIfEnrollmentIdIsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.delete) + .build()); + assertThat(ex.getMessage(), containsString("enrollmentId not set")); + } + + @Test + void testEnrollDeleteWithEnrollmentIdProducesCorrectCommand() { + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.delete) + .enrollmentId(createEnrollmentId("abc123")) + .build(); + + assertThat(result, is("enroll:delete{\"enrollmentId\":\"abc123\"}")); + } + + @Test + void testEnrollRequestThrowExceptionIfFieldsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.request) + .deviceName("myDevice") + .apkamPublicKey("pubKey") + .build()); + assertThat(ex.getMessage(), containsString("appName not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.request) + .appName("") + .deviceName("myDevice") + .apkamPublicKey("pubKey") + .build()); + assertThat(ex.getMessage(), containsString("appName not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.request) + .appName("myApp") + .apkamPublicKey("pubKey") + .build()); + assertThat(ex.getMessage(), containsString("deviceName not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.request) + .appName("myApp") + .deviceName("") + .apkamPublicKey("pubKey") + .build()); + assertThat(ex.getMessage(), containsString("deviceName not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.request) + .appName("myApp") + .deviceName("myDevice") + .build()); + assertThat(ex.getMessage(), containsString("apkamPublicKey not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.request) + .otp("") + .appName("myApp") + .deviceName("myDevice") + .apkamPublicKey("pubKey") + .build()); + assertThat(ex.getMessage(), containsString("otp not set")); + + ex = assertThrows(IllegalArgumentException.class, () -> VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.request) + .otp("AZ19") + .appName("myApp") + .deviceName("myDevice") + .apkamPublicKey("pubKey") + .build()); + assertThat(ex.getMessage(), containsString("namespaces not set")); + } + + @Test + void testInitialEnrollRequestReturnsExpectedCommand() { + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.request) + .appName("myApp") + .deviceName("myDevice") + .apkamPublicKey("pubKey") + .build(); + + assertThat(result, is("enroll:request{" + + "\"appName\":\"myApp\"," + + "\"deviceName\":\"myDevice\"," + + "\"apkamPublicKey\":\"pubKey\"" + + "}")); + } + + @Test + void testSubsequentEnrollRequestReturnsExpectedCommand() { + Map namespaces = new LinkedHashMap<>(); + namespaces.put("ns1", "rw"); + + String result = VerbBuilders.enrollCommandBuilder() + .operation(EnrollOperation.request) + .appName("myApp") + .deviceName("myDevice") + .apkamPublicKey("pubKey") + .apkamSymmetricKey("symKey") + .otp("123456") + .namespaces(namespaces) + .ttl(86400000L) + .build(); + + assertThat(result, is("enroll:request{" + + "\"appName\":\"myApp\"," + + "\"deviceName\":\"myDevice\"," + + "\"apkamPublicKey\":\"pubKey\"," + + "\"encryptedAPKAMSymmetricKey\":\"symKey\"," + + "\"otp\":\"123456\"," + + "\"namespaces\":{\"ns1\":\"rw\"}," + + "\"apkamKeysExpiryInMillis\":86400000" + + "}")); + } + + @Test + public void testOtpBuilderGeneratesExpectedOutput() { + String command = VerbBuilders.otpCommandBuilder().build(); + assertEquals("otp:get", command); + } + + @Test + void testKeysBuilderThrowsExceptionIfFieldsNotSet() { + IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.keysCommandBuilder().build()); + assertThat(ex.getMessage(), containsString("operation not set")); + + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.keysCommandBuilder().operation(VerbBuilders.KeysOperation.get).build()); + assertThat(ex.getMessage(), containsString("keyName not set")); + + ex = assertThrows(IllegalArgumentException.class, + () -> VerbBuilders.keysCommandBuilder() + .operation(VerbBuilders.KeysOperation.delete) + .keyName("private:_secret@fred") + .build()); + assertThat(ex.getMessage(), containsString("delete not supported")); + } + + + @Test + void testKeysBuilderReturnsExpectedCommand() { + String command = VerbBuilders.keysCommandBuilder() + .operation(VerbBuilders.KeysOperation.get) + .keyName("private:_secret@fred") + .build(); + assertThat(command, equalTo("keys:get:keyName:private:_secret@fred")); } } diff --git a/at_client/src/test/java/org/atsign/cucumber/steps/GetAtKeysSteps.java b/at_client/src/test/java/org/atsign/cucumber/steps/GetAtKeysSteps.java index 264c4d4c..1f1cdb7d 100644 --- a/at_client/src/test/java/org/atsign/cucumber/steps/GetAtKeysSteps.java +++ b/at_client/src/test/java/org/atsign/cucumber/steps/GetAtKeysSteps.java @@ -11,7 +11,6 @@ import org.atsign.client.api.AtClient; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; import org.atsign.common.Keys; import io.cucumber.datatable.DataTable; @@ -210,12 +209,14 @@ private void dumpKeys(AtClient atClient, List headings, boolean fetchMet private String lookupStringValue(AtClient atClient, Keys.AtKey key) { try { - if (key.sharedWith != null) { - return atClient.get(new KeyBuilders.SharedKeyBuilder(key.sharedBy, key.sharedWith).key(key.name).build()).get(); - } else if (key.metadata.isPublic) { - return atClient.get(new KeyBuilders.PublicKeyBuilder(key.sharedBy).key(key.name).build()).get(); + if (key instanceof Keys.SharedKey) { + return atClient.get((Keys.SharedKey) key).get(); + } else if (key instanceof Keys.PublicKey) { + return atClient.get((Keys.PublicKey) key).get(); + } else if (key instanceof Keys.SelfKey) { + return atClient.get((Keys.SelfKey) key).get(); } else { - return atClient.get(new KeyBuilders.SelfKeyBuilder(key.sharedBy).key(key.name).build()).get(); + return key.getClass().getSimpleName(); } } catch (Exception e) { return e.getMessage(); @@ -233,88 +234,88 @@ private List toDataTableRow(List headings, Keys.AtKey k, String row.add(value); break; case "name": - row.add(k.name); + row.add(k.nameWithoutNamespace()); break; case "namespace": - row.add(k.getNamespace()); + row.add(k.namespace()); break; case "sharedby": - row.add(k.sharedBy != null ? k.sharedBy.withoutPrefix() : null); + row.add(withoutPrefix(k.sharedBy())); break; case "sharedwith": - row.add(k.sharedWith != null ? k.sharedWith.withoutPrefix() : null); + row.add(withoutPrefix(k.sharedWith())); break; case "ttl": - row.add(k.metadata != null ? String.valueOf(k.metadata.ttl) : null); + row.add(toString(k.metadata().ttl())); break; case "ttb": - row.add(k.metadata != null ? String.valueOf(k.metadata.ttb) : null); + row.add(toString(k.metadata().ttb())); break; case "ttr": - row.add(k.metadata != null ? String.valueOf(k.metadata.ttr) : null); + row.add(toString(k.metadata().ttb())); break; case "ccd": - row.add(k.metadata != null ? String.valueOf(k.metadata.ccd) : null); + row.add(toString(k.metadata().ccd())); break; case "createdby": - row.add(k.metadata != null ? k.metadata.createdBy : null); + row.add(toString(k.metadata().createdBy())); break; case "updatedby": - row.add(k.metadata != null ? k.metadata.updatedBy : null); + row.add(toString(k.metadata().updatedBy())); break; case "availableat": - row.add(k.metadata != null ? String.valueOf(k.metadata.availableAt) : null); + row.add(toString(k.metadata().availableAt())); break; case "expiresat": - row.add(k.metadata != null ? String.valueOf(k.metadata.expiresAt) : null); + row.add(toString(k.metadata().expiresAt())); break; case "refreshat": - row.add(k.metadata != null ? String.valueOf(k.metadata.refreshAt) : null); + row.add(toString(k.metadata().refreshAt())); break; case "createdat": - row.add(k.metadata != null ? String.valueOf(k.metadata.createdAt) : null); + row.add(toString(k.metadata().createdAt())); break; case "updatedat": - row.add(k.metadata != null ? String.valueOf(k.metadata.updatedAt) : null); + row.add(toString(k.metadata().updatedAt())); break; case "status": - row.add(k.metadata != null ? k.metadata.status : null); + row.add(toString(k.metadata().status())); break; case "version": - row.add(k.metadata != null ? String.valueOf(k.metadata.version) : null); + row.add(toString(k.metadata().version())); break; case "datasignature": - row.add(k.metadata != null ? k.metadata.dataSignature : null); + row.add(toString(k.metadata().dataSignature())); break; case "sharedkeystatus": - row.add(k.metadata != null ? k.metadata.sharedKeyStatus : null); + row.add(toString(k.metadata().sharedKeyStatus())); break; case "ispublic": - row.add(k.metadata != null ? String.valueOf(k.metadata.isPublic) : null); + row.add(toString(k.metadata().isPublic())); break; case "isencrypted": - row.add(k.metadata != null ? String.valueOf(k.metadata.isEncrypted) : null); + row.add(toString(k.metadata().isEncrypted())); break; case "ishidden": - row.add(k.metadata != null ? String.valueOf(k.metadata.isHidden) : null); + row.add(toString(k.metadata().isHidden())); break; case "namespaceaware": - row.add(k.metadata != null ? String.valueOf(k.metadata.namespaceAware) : null); + row.add(toString(k.metadata().namespaceAware())); break; case "isbinary": - row.add(k.metadata != null ? String.valueOf(k.metadata.isBinary) : null); + row.add(toString(k.metadata().isBinary())); break; case "iscached": - row.add(k.metadata != null ? String.valueOf(k.metadata.isCached) : null); + row.add(toString(k.metadata().isCached())); break; case "sharedkeyenc": - row.add(k.metadata != null ? k.metadata.sharedKeyEnc : null); + row.add(toString(k.metadata().sharedKeyEnc())); break; case "pubkeycs": - row.add(k.metadata != null ? k.metadata.pubKeyCS : null); + row.add(toString(k.metadata().pubKeyCS())); break; case "encoding": - row.add(k.metadata != null ? k.metadata.encoding : null); + row.add(toString(k.metadata().encoding())); break; default: throw new IllegalArgumentException(heading + " not recognised as a key or key metadata field"); @@ -337,4 +338,12 @@ private List> getAtKeysAsListOfMaps(AtClient client, String return actual; } + private static String withoutPrefix(AtSign atSign) { + return atSign != null ? atSign.withoutPrefix() : null; + } + + private static String toString(Object o) { + return o != null ? o.toString() : null; + } + } diff --git a/at_client/src/test/java/org/atsign/cucumber/steps/PublicAtKeySteps.java b/at_client/src/test/java/org/atsign/cucumber/steps/PublicAtKeySteps.java index 2dc611a8..12ca8568 100644 --- a/at_client/src/test/java/org/atsign/cucumber/steps/PublicAtKeySteps.java +++ b/at_client/src/test/java/org/atsign/cucumber/steps/PublicAtKeySteps.java @@ -7,9 +7,7 @@ import java.util.regex.Matcher; import org.atsign.client.api.AtClient; -import org.atsign.client.util.KeyStringUtil; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; import org.atsign.common.Keys; import io.cucumber.java.en.Then; @@ -205,20 +203,20 @@ private void deleteKeyValue(AtClient atClient, AtSign owner, String name) throws } private Keys.PublicKey toKey(AtSign sharedBy, String s) { - KeyBuilders.PublicKeyBuilder builder = new KeyBuilders.PublicKeyBuilder(sharedBy); - Matcher matcher = KeyStringUtil.createNamespaceQualifiedKeyNameMatcher(s); + Keys.PublicKeyBuilder builder = Keys.publicKeyBuilder() + .sharedBy(sharedBy) + .ttl(context.getKeyTtl()); + Matcher matcher = Keys.createNamespaceQualifiedKeyNameMatcher(s); if (matcher.matches()) { if (context.isNamespaceSet()) { throw new IllegalArgumentException("context has namespace set, intention is ambiguous"); } - builder.namespace(matcher.group(2)).key(matcher.group(1)); + builder.namespace(matcher.group(2)).name(matcher.group(1)); } else if (context.isNamespaceSet()) { - builder.namespace(context.getNamespace()).key(s); + builder.namespace(context.getNamespace()).name(s); } else { - builder.key(s); + builder.name(s); } - Keys.PublicKey key = builder.build(); - key.metadata.ttl = (int) context.getKeyTtl(); - return key; + return builder.build(); } } diff --git a/at_client/src/test/java/org/atsign/cucumber/steps/SelfAtKeySteps.java b/at_client/src/test/java/org/atsign/cucumber/steps/SelfAtKeySteps.java index d6ecf162..4594a193 100644 --- a/at_client/src/test/java/org/atsign/cucumber/steps/SelfAtKeySteps.java +++ b/at_client/src/test/java/org/atsign/cucumber/steps/SelfAtKeySteps.java @@ -7,9 +7,7 @@ import java.util.regex.Matcher; import org.atsign.client.api.AtClient; -import org.atsign.client.util.KeyStringUtil; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; import org.atsign.common.Keys; import io.cucumber.java.en.Then; @@ -150,20 +148,20 @@ private void deleteKeyValue(AtClient atClient, AtSign atSign, String name) throw } private Keys.SelfKey createKey(AtSign owner, String s) { - KeyBuilders.SelfKeyBuilder builder = new KeyBuilders.SelfKeyBuilder(owner); - Matcher matcher = KeyStringUtil.createNamespaceQualifiedKeyNameMatcher(s); + Keys.SelfKeyBuilder builder = Keys.selfKeyBuilder() + .sharedBy(owner) + .ttl(context.getKeyTtl()); + Matcher matcher = Keys.createNamespaceQualifiedKeyNameMatcher(s); if (matcher.matches()) { if (context.isNamespaceSet()) { throw new IllegalArgumentException("context has namespace set, intention is ambiguous"); } - builder.namespace(matcher.group(2)).key(matcher.group(1)); + builder.namespace(matcher.group(2)).name(matcher.group(1)); } else if (context.isNamespaceSet()) { - builder.namespace(context.getNamespace()).key(s); + builder.namespace(context.getNamespace()).name(s); } else { - builder.key(s); + builder.name(s); } - Keys.SelfKey key = builder.build(); - key.metadata.ttl = (int) context.getKeyTtl(); - return key; + return builder.build(); } } diff --git a/at_client/src/test/java/org/atsign/cucumber/steps/SharedAtKeySteps.java b/at_client/src/test/java/org/atsign/cucumber/steps/SharedAtKeySteps.java index 624c92f7..a65fe116 100644 --- a/at_client/src/test/java/org/atsign/cucumber/steps/SharedAtKeySteps.java +++ b/at_client/src/test/java/org/atsign/cucumber/steps/SharedAtKeySteps.java @@ -6,9 +6,7 @@ import java.util.regex.Matcher; import org.atsign.client.api.AtClient; -import org.atsign.client.util.KeyStringUtil; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; import org.atsign.common.Keys; import io.cucumber.java.en.Then; @@ -206,21 +204,22 @@ private void deleteKeyValue(AtClient atClient, AtSign sharedBy, String name, AtS } private Keys.SharedKey createKey(AtSign sharedBy, String s, AtSign sharedWith) { - KeyBuilders.SharedKeyBuilder builder = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith); - Matcher matcher = KeyStringUtil.createNamespaceQualifiedKeyNameMatcher(s); + Keys.SharedKeyBuilder builder = Keys.sharedKeyBuilder() + .sharedBy(sharedBy) + .sharedWith(sharedWith) + .ttl(context.getKeyTtl()); + Matcher matcher = Keys.createNamespaceQualifiedKeyNameMatcher(s); if (matcher.matches()) { if (context.isNamespaceSet()) { throw new IllegalArgumentException("context has namespace set, intention is ambiguous"); } - builder.namespace(matcher.group(2)).key(matcher.group(1)); + builder.namespace(matcher.group(2)).name(matcher.group(1)); } else if (context.isNamespaceSet()) { - builder.namespace(context.getNamespace()).key(s); + builder.namespace(context.getNamespace()).name(s); } else { - builder.key(s); + builder.name(s); } - Keys.SharedKey key = builder.build(); - key.metadata.ttl = (int) context.getKeyTtl(); - return key; + return builder.build(); } } diff --git a/at_client/src/test/resources/features/SelfKey.feature b/at_client/src/test/resources/features/SelfKey.feature index 0165a5ec..d12e3d33 100644 --- a/at_client/src/test/resources/features/SelfKey.feature +++ b/at_client/src/test/resources/features/SelfKey.feature @@ -31,7 +31,6 @@ Feature: AtClient API tests for SelfKeys | test@gary | Scenario: SelfKeys are invisible to other at signs - And dump keys And AtClient.put for SelfKey test and value "hello world" Then @colin AtClient.getAtKeys for ".+" does NOT contain | test@gary | diff --git a/at_client/src/test/resources/features/SharedKey.feature b/at_client/src/test/resources/features/SharedKey.feature index 25fec692..bae5448b 100644 --- a/at_client/src/test/resources/features/SharedKey.feature +++ b/at_client/src/test/resources/features/SharedKey.feature @@ -52,3 +52,9 @@ Feature: AtClient API test for SharedKeys And @colin AtClient.put for SharedKey message.ns shared with @gary and value "hi gary it's colin" Then @colin AtClient.get for SharedKey message.ns shared by @gary returns value that matches "hi colin it's gary" And @gary AtClient.get for SharedKey message.ns shared by @colin returns value that matches "hi gary it's colin" + + Scenario: SharedKey get returns expected value for "Shared With" atsign after change + When AtClient.put for SharedKey test shared with @colin and value "hello world" + And @colin AtClient.get for SharedKey test shared by @gary returns value that matches "hello world" + And AtClient.put for SharedKey test shared with @colin and value "goodbye cruel world" + Then @colin AtClient.get for SharedKey test shared by @gary returns value that matches "goodbye cruel world" diff --git a/at_shell/src/main/java/org/atsign/client/cli/REPL.java b/at_shell/src/main/java/org/atsign/client/cli/REPL.java index 90d4f4d1..38931828 100644 --- a/at_shell/src/main/java/org/atsign/client/cli/REPL.java +++ b/at_shell/src/main/java/org/atsign/client/cli/REPL.java @@ -12,7 +12,6 @@ import org.atsign.client.api.AtEvents.AtEventType; import org.atsign.client.api.Secondary; import org.atsign.client.util.ArgsUtil; -import org.atsign.client.util.KeyStringUtil; import org.atsign.client.util.KeysUtil; import org.atsign.common.AtException; import org.atsign.common.AtSign; @@ -100,21 +99,17 @@ public void repl() throws AtException { printHelpInstructions(); } else if ("get".equals(verb)) { String fullKeyName = parts[1]; - KeyStringUtil keyStringUtil = new KeyStringUtil(fullKeyName); - KeyStringUtil.KeyType keyType = keyStringUtil.getKeyType(); - if (keyType.equals(KeyStringUtil.KeyType.PUBLIC_KEY)) { - PublicKey pk = (PublicKey) Keys.fromString(fullKeyName); - String value = client.get(pk).get(); + Keys.AtKey key = Keys.keyBuilder().rawKey(fullKeyName).build(); + if (key instanceof PublicKey) { + String value = client.get((PublicKey) key).get(); System.out.println(" => \033[31m" + value + "\033[0m"); - } else if (keyType.equals(KeyStringUtil.KeyType.SELF_KEY)) { - SelfKey sk = (SelfKey) Keys.fromString(fullKeyName); - String value = client.get(sk).get(); + } else if (key instanceof SelfKey) { + String value = client.get((SelfKey) key).get(); System.out.println(" => \033[31m" + value + "\033[0m"); - } else if (keyType.equals(KeyStringUtil.KeyType.SHARED_KEY)) { - SharedKey sk = Keys.SharedKey.fromString(fullKeyName); - String value = client.get(sk).get(); + } else if (key instanceof SharedKey) { + String value = client.get((SharedKey) key).get(); System.out.println(" => \033[31m" + value + "\033[0m"); - } else if (keyType.equals(KeyStringUtil.KeyType.PRIVATE_HIDDEN_KEY)) { + } else if (key instanceof Keys.PrivateHiddenKey) { throw new UnsupportedOperationException("PrivateHiddenKey is not implemented yet"); } else { throw new AtInvalidSyntaxException("Could not evaluate the key type of: " + fullKeyName); @@ -122,21 +117,17 @@ public void repl() throws AtException { } else if ("put".equals(verb)) { String fullKeyName = parts[1]; String value = command.substring(verb.length() + fullKeyName.length() + 2).trim(); - KeyStringUtil keyStringUtil = new KeyStringUtil(fullKeyName); - KeyStringUtil.KeyType keyType = keyStringUtil.getKeyType(); - if (keyType.equals(KeyStringUtil.KeyType.PUBLIC_KEY)) { - PublicKey pk = (PublicKey) Keys.fromString(fullKeyName); - String data = client.put(pk, value).get(); + Keys.AtKey key = Keys.keyBuilder().rawKey(fullKeyName).build(); + if (key instanceof PublicKey) { + String data = client.put((PublicKey) key, value).get(); System.out.println(" => \033[31m" + data + "\033[0m"); - } else if (keyType.equals(KeyStringUtil.KeyType.SELF_KEY)) { - SelfKey sk = (SelfKey) Keys.fromString(fullKeyName); - String data = client.put(sk, value).get(); + } else if (key instanceof SelfKey) { + String data = client.put((SelfKey) key, value).get(); System.out.println(" => \033[31m" + data + "\033[0m"); - } else if (keyType.equals(KeyStringUtil.KeyType.SHARED_KEY)) { - SharedKey sk = Keys.SharedKey.fromString(fullKeyName); - String data = client.put(sk, value).get(); + } else if (key instanceof SharedKey) { + String data = client.put((SharedKey) key, value).get(); System.out.println(" => \033[31m" + data + "\033[0m"); - } else if (keyType.equals(KeyStringUtil.KeyType.PRIVATE_HIDDEN_KEY)) { + } else if (key instanceof Keys.PrivateHiddenKey) { throw new UnsupportedOperationException("PrivateHiddenKey is not implemented yet"); } else { throw new AtIllegalArgumentException("Could not evaluate the key type of: " + fullKeyName); @@ -150,21 +141,17 @@ public void repl() throws AtException { System.out.println(" => \033[31m" + value + "\033[0m"); } else if ("delete".equals(verb)) { String fullKeyName = parts[1]; - KeyStringUtil keyStringUtil = new KeyStringUtil(fullKeyName); - KeyStringUtil.KeyType keyType = keyStringUtil.getKeyType(); - if (keyType.equals(KeyStringUtil.KeyType.PUBLIC_KEY)) { - PublicKey pk = (PublicKey) Keys.fromString(fullKeyName); - String data = client.delete(pk).get(); + Keys.AtKey key = Keys.keyBuilder().rawKey(fullKeyName).build(); + if (key instanceof PublicKey) { + String data = client.delete((PublicKey) key).get(); System.out.println(" => \033[31m" + data + "\033[0m"); - } else if (keyType.equals(KeyStringUtil.KeyType.SELF_KEY)) { - SelfKey sk = (SelfKey) Keys.fromString(fullKeyName); - String data = client.delete(sk).get(); + } else if (key instanceof SelfKey) { + String data = client.delete((SelfKey) key).get(); System.out.println(" => \033[31m" + data + "\033[0m"); - } else if (keyType.equals(KeyStringUtil.KeyType.SHARED_KEY)) { - SharedKey sk = Keys.SharedKey.fromString(fullKeyName); - String data = client.delete(sk).get(); + } else if (key instanceof SharedKey) { + String data = client.delete((SharedKey) key).get(); System.out.println(" => \033[31m" + data + "\033[0m"); - } else if (keyType.equals(KeyStringUtil.KeyType.PRIVATE_HIDDEN_KEY)) { + } else if (key instanceof Keys.PrivateHiddenKey) { throw new UnsupportedOperationException("PrivateHiddenKey is not implemented yet"); } else { throw new AtIllegalArgumentException("Could not evaluate the key type of: " + fullKeyName); @@ -214,7 +201,7 @@ public void handleEvent(AtEventType eventType, Map eventData) { String decryptedValue; switch (eventType) { case decryptedUpdateNotification: - sharedKey = Keys.SharedKey.fromString((String) eventData.get("key")); + sharedKey = Keys.sharedKeyBuilder().rawKey((String) eventData.get("key")).build(); value = (String) eventData.get("value"); decryptedValue = (String) eventData.get("decryptedValue"); System.out.println(" => Notification ==> \033[31m Key: [" + sharedKey + "] ==> EncryptedValue [" + value @@ -227,7 +214,7 @@ public void handleEvent(AtEventType eventType, Map eventData) { break; case updateNotification: try { - sharedKey = Keys.SharedKey.fromString((String) eventData.get("key")); + sharedKey = Keys.sharedKeyBuilder().rawKey((String) eventData.get("key")).build(); String encryptedValue = (String) eventData.get("value"); decryptedValue = client.get(sharedKey).get(); System.out.println(" => Notification ==> \033[31m Key: [" + sharedKey + "] ==> EncryptedValue [" diff --git a/examples/src/main/java/org/atsign/examples/PublicKeyDeleteExample.java b/examples/src/main/java/org/atsign/examples/PublicKeyDeleteExample.java index c299bae0..3ee3a1ca 100644 --- a/examples/src/main/java/org/atsign/examples/PublicKeyDeleteExample.java +++ b/examples/src/main/java/org/atsign/examples/PublicKeyDeleteExample.java @@ -8,7 +8,7 @@ import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.PublicKey; public class PublicKeyDeleteExample { @@ -28,7 +28,7 @@ public static void main(String[] args) { try (AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign, loadKeys(atSign), VERBOSE)) { // 4. create public key - PublicKey pk = new KeyBuilders.PublicKeyBuilder(atSign).key(KEY_NAME).build(); + PublicKey pk = Keys.publicKeyBuilder().sharedBy(atSign).name(KEY_NAME).build(); // 5. delete the key String response = atClient.delete(pk).get(); diff --git a/examples/src/main/java/org/atsign/examples/PublicKeyGetBypassCacheExample.java b/examples/src/main/java/org/atsign/examples/PublicKeyGetBypassCacheExample.java index 6854d018..21ad8494 100644 --- a/examples/src/main/java/org/atsign/examples/PublicKeyGetBypassCacheExample.java +++ b/examples/src/main/java/org/atsign/examples/PublicKeyGetBypassCacheExample.java @@ -6,7 +6,7 @@ import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.PublicKey; import org.atsign.common.options.GetRequestOptions; @@ -28,7 +28,7 @@ public static void main(String[] args) { try (AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign, loadKeys(atSign), VERBOSE)) { // 4. create the key - PublicKey pk = new KeyBuilders.PublicKeyBuilder(new AtSign("@bob")).key(KEY_NAME).build(); + PublicKey pk = Keys.publicKeyBuilder().sharedBy(new AtSign("@bob")).name(KEY_NAME).build(); // 5. get the value associated with the key String response = atClient.get(pk, (GetRequestOptions) new GetRequestOptions().bypassCache(true).build()).get(); diff --git a/examples/src/main/java/org/atsign/examples/PublicKeyGetExample.java b/examples/src/main/java/org/atsign/examples/PublicKeyGetExample.java index b983f5b6..a904e83d 100644 --- a/examples/src/main/java/org/atsign/examples/PublicKeyGetExample.java +++ b/examples/src/main/java/org/atsign/examples/PublicKeyGetExample.java @@ -6,7 +6,7 @@ import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.PublicKey; import static org.atsign.client.util.KeysUtil.loadKeys; @@ -27,7 +27,7 @@ public static void main(String[] args) { try (AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign, loadKeys(atSign), VERBOSE)) { // 4. create the key - PublicKey pk = new KeyBuilders.PublicKeyBuilder(atSign).key(KEY_NAME).build(); + PublicKey pk = Keys.publicKeyBuilder().sharedBy(atSign).name(KEY_NAME).build(); // 5. get the value associated with the key String response = atClient.get(pk).get(); diff --git a/examples/src/main/java/org/atsign/examples/PublicKeyPutExample.java b/examples/src/main/java/org/atsign/examples/PublicKeyPutExample.java index 6464a33f..067e22ae 100644 --- a/examples/src/main/java/org/atsign/examples/PublicKeyPutExample.java +++ b/examples/src/main/java/org/atsign/examples/PublicKeyPutExample.java @@ -1,16 +1,16 @@ package org.atsign.examples; +import static org.atsign.client.util.KeysUtil.loadKeys; + import java.io.IOException; import java.util.concurrent.ExecutionException; import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.PublicKey; -import static org.atsign.client.util.KeysUtil.loadKeys; - public class PublicKeyPutExample { public static void main(String[] args) { @@ -29,7 +29,7 @@ public static void main(String[] args) { try (AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign, loadKeys(atSign), VERBOSE)) { // 4. create a new public key - PublicKey pk = new KeyBuilders.PublicKeyBuilder(atSign).key(KEY_NAME).build(); + PublicKey pk = Keys.publicKeyBuilder().sharedBy(atSign).name(KEY_NAME).build(); // 5. put the key String response = atClient.put(pk, VALUE).get(); diff --git a/examples/src/main/java/org/atsign/examples/SelfKeyDeleteExample.java b/examples/src/main/java/org/atsign/examples/SelfKeyDeleteExample.java index efb0d508..5b5c97e1 100644 --- a/examples/src/main/java/org/atsign/examples/SelfKeyDeleteExample.java +++ b/examples/src/main/java/org/atsign/examples/SelfKeyDeleteExample.java @@ -1,16 +1,16 @@ package org.atsign.examples; +import static org.atsign.client.util.KeysUtil.loadKeys; + import java.io.IOException; import java.util.concurrent.ExecutionException; import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.SelfKey; -import static org.atsign.client.util.KeysUtil.loadKeys; - public class SelfKeyDeleteExample { public static void main(String[] args) { @@ -28,7 +28,7 @@ public static void main(String[] args) { try (AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign, loadKeys(atSign), VERBOSE)) { // 4. create self key - SelfKey sk = new KeyBuilders.SelfKeyBuilder(atSign).key(KEY_NAME).build(); + SelfKey sk = Keys.selfKeyBuilder().sharedBy(atSign).name(KEY_NAME).build(); // 5. delete the key String response = atClient.delete(sk).get(); diff --git a/examples/src/main/java/org/atsign/examples/SelfKeyGetExample.java b/examples/src/main/java/org/atsign/examples/SelfKeyGetExample.java index b8b0aee4..edf82c04 100644 --- a/examples/src/main/java/org/atsign/examples/SelfKeyGetExample.java +++ b/examples/src/main/java/org/atsign/examples/SelfKeyGetExample.java @@ -1,17 +1,17 @@ package org.atsign.examples; +import static org.atsign.client.util.KeysUtil.loadKeys; + import java.io.IOException; import java.util.concurrent.ExecutionException; import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.SelfKey; import org.atsign.common.Metadata; -import static org.atsign.client.util.KeysUtil.loadKeys; - public class SelfKeyGetExample { public static void main(String[] args) { @@ -29,12 +29,12 @@ public static void main(String[] args) { try (AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign, loadKeys(atSign), VERBOSE)) { // 4. create selfkey - SelfKey sk = new KeyBuilders.SelfKeyBuilder(atSign).key(KEY_NAME).build(); + SelfKey sk = Keys.selfKeyBuilder().sharedBy(atSign).name(KEY_NAME).build(); // 5. get the key String response = atClient.get(sk).get(); System.out.println(response); - _printMetadata(sk.metadata); + _printMetadata(sk.metadata()); } catch (AtException | IOException | InterruptedException | ExecutionException e) { System.err.println("Failed to connect to remote server " + e); @@ -44,26 +44,24 @@ public static void main(String[] args) { private static void _printMetadata(Metadata metadata) { - System.out.println("ttl: " + metadata.ttl); - System.out.println("ttb: " + metadata.ttb); - System.out.println("ttr: " + metadata.ttr); - System.out.println("ccd: " + metadata.ccd); - System.out.println("availableAt: " + (metadata.availableAt != null ? metadata.availableAt.toString() : "null")); - System.out.println("expiresAt: " + (metadata.expiresAt != null ? metadata.expiresAt.toString() : "null")); - System.out.println("refreshAt: " + (metadata.refreshAt != null ? metadata.refreshAt.toString() : "null")); - System.out.println("createdAt: " + (metadata.createdAt != null ? metadata.createdAt.toString() : "null")); - System.out.println("updatedAt: " + (metadata.updatedAt != null ? metadata.updatedAt.toString() : "null")); - System.out.println("dataSignature: " + metadata.dataSignature); - System.out.println("sharedKeyStatus: " + metadata.sharedKeyStatus); - System.out.println("isPublic: " + metadata.isPublic); - System.out.println("isEncrypted: " + metadata.isEncrypted); - System.out.println("isHidden: " + metadata.isHidden); - System.out.println("namespaceAware: " + metadata.namespaceAware); - System.out.println("isBinary: " + metadata.isBinary); - System.out.println("isCached: " + metadata.isCached); - System.out.println("sharedKeyEnc: " + metadata.sharedKeyEnc); - System.out.println("pubKeyCS: " + metadata.pubKeyCS); + System.out.println("ttl: " + metadata.ttl()); + System.out.println("ttb: " + metadata.ttb()); + System.out.println("ttr: " + metadata.ttr()); + System.out.println("ccd: " + metadata.ccd()); + System.out.println("availableAt: " + (metadata.availableAt() != null ? metadata.availableAt().toString() : "null")); + System.out.println("expiresAt: " + (metadata.expiresAt() != null ? metadata.expiresAt().toString() : "null")); + System.out.println("refreshAt: " + (metadata.refreshAt() != null ? metadata.refreshAt().toString() : "null")); + System.out.println("createdAt: " + (metadata.createdAt() != null ? metadata.createdAt().toString() : "null")); + System.out.println("updatedAt: " + (metadata.updatedAt() != null ? metadata.updatedAt().toString() : "null")); + System.out.println("dataSignature: " + metadata.dataSignature()); + System.out.println("sharedKeyStatus: " + metadata.sharedKeyStatus()); + System.out.println("isPublic: " + metadata.isPublic()); + System.out.println("isEncrypted: " + metadata.isEncrypted()); + System.out.println("isHidden: " + metadata.isHidden()); + System.out.println("namespaceAware: " + metadata.namespaceAware()); + System.out.println("isBinary: " + metadata.isBinary()); + System.out.println("isCached: " + metadata.isCached()); + System.out.println("sharedKeyEnc: " + metadata.sharedKeyEnc()); + System.out.println("pubKeyCS: " + metadata.pubKeyCS()); } - - } diff --git a/examples/src/main/java/org/atsign/examples/SelfKeyPutExample.java b/examples/src/main/java/org/atsign/examples/SelfKeyPutExample.java index cbd1c8d0..c4d377b2 100644 --- a/examples/src/main/java/org/atsign/examples/SelfKeyPutExample.java +++ b/examples/src/main/java/org/atsign/examples/SelfKeyPutExample.java @@ -1,16 +1,17 @@ package org.atsign.examples; +import static org.atsign.client.util.KeysUtil.loadKeys; + import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.SelfKey; -import static org.atsign.client.util.KeysUtil.loadKeys; - public class SelfKeyPutExample { public static void main(String[] args) { @@ -21,7 +22,7 @@ public static void main(String[] args) { String KEY_NAME = "test"; String VALUE = "I hate pineapple on pizza!!!"; - int ttl = 30 * 60 * 1000; + long ttl = TimeUnit.SECONDS.toMillis(30); // 2. create AtSign object @@ -31,8 +32,7 @@ public static void main(String[] args) { try (AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, atSign, loadKeys(atSign), VERBOSE)) { // 4. create selfkey - SelfKey sk = new KeyBuilders.SelfKeyBuilder(atSign).key(KEY_NAME).build(); - sk.metadata.ttl = ttl; + SelfKey sk = Keys.selfKeyBuilder().sharedBy(atSign).name(KEY_NAME).ttl(ttl).build(); // 5. put the key String response = atClient.put(sk, VALUE).get(); diff --git a/examples/src/main/java/org/atsign/examples/SharedKeyDeleteExample.java b/examples/src/main/java/org/atsign/examples/SharedKeyDeleteExample.java index d3d6b188..dd8e57c6 100644 --- a/examples/src/main/java/org/atsign/examples/SharedKeyDeleteExample.java +++ b/examples/src/main/java/org/atsign/examples/SharedKeyDeleteExample.java @@ -1,16 +1,16 @@ package org.atsign.examples; +import static org.atsign.client.util.KeysUtil.loadKeys; + import java.io.IOException; import java.util.concurrent.ExecutionException; import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.SharedKey; -import static org.atsign.client.util.KeysUtil.loadKeys; - public class SharedKeyDeleteExample { /// Delete a SharedKey that you shared with another atSign, the key must be on your own secondary server (belonging to the sharedBy atSign) @@ -30,7 +30,7 @@ public static void main(String[] args) { try (AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, sharedBy, loadKeys(sharedBy), VERBOSE)) { // 4. create SharedKey instance - SharedKey sk = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key(KEY_NAME).build(); + SharedKey sk = Keys.sharedKeyBuilder().sharedBy(sharedBy).sharedWith(sharedWith).name(KEY_NAME).build(); // 5. delete the key String response = atClient.delete(sk).get(); diff --git a/examples/src/main/java/org/atsign/examples/SharedKeyGetOtherExample.java b/examples/src/main/java/org/atsign/examples/SharedKeyGetOtherExample.java index e0695f42..9ab6e8e5 100644 --- a/examples/src/main/java/org/atsign/examples/SharedKeyGetOtherExample.java +++ b/examples/src/main/java/org/atsign/examples/SharedKeyGetOtherExample.java @@ -1,16 +1,16 @@ package org.atsign.examples; +import static org.atsign.client.util.KeysUtil.loadKeys; + import java.io.IOException; import java.util.concurrent.ExecutionException; import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.SharedKey; -import static org.atsign.client.util.KeysUtil.loadKeys; - public class SharedKeyGetOtherExample { /// Get the SharedKey sharedBy another person and sharedWith you public static void main(String[] args) { @@ -30,7 +30,7 @@ public static void main(String[] args) { // 4. create SharedKey instance // key is sharedBy the other person and sharedWith you. - SharedKey sk = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key(KEY_NAME).build(); + SharedKey sk = Keys.sharedKeyBuilder().sharedBy(sharedBy).sharedWith(sharedWith).name(KEY_NAME).build(); // 5. get the key String response = atClient.get(sk).get(); diff --git a/examples/src/main/java/org/atsign/examples/SharedKeyGetSelfExample.java b/examples/src/main/java/org/atsign/examples/SharedKeyGetSelfExample.java index d950364c..8012f449 100644 --- a/examples/src/main/java/org/atsign/examples/SharedKeyGetSelfExample.java +++ b/examples/src/main/java/org/atsign/examples/SharedKeyGetSelfExample.java @@ -1,16 +1,16 @@ package org.atsign.examples; +import static org.atsign.client.util.KeysUtil.loadKeys; + import java.io.IOException; import java.util.concurrent.ExecutionException; import org.atsign.client.api.AtClient; import org.atsign.common.AtException; import org.atsign.common.AtSign; -import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; import org.atsign.common.Keys.SharedKey; -import static org.atsign.client.util.KeysUtil.loadKeys; - public class SharedKeyGetSelfExample { /// Get a SharedKey that you created and shared with another atSign public static void main(String[] args) { @@ -29,7 +29,7 @@ public static void main(String[] args) { try (AtClient atClient = AtClient.withRemoteSecondary(ROOT_URL, sharedBy, loadKeys(sharedBy), VERBOSE)) { // 4. create SharedKey instance - SharedKey sk = new KeyBuilders.SharedKeyBuilder(sharedBy, sharedWith).key(KEY_NAME).build(); + SharedKey sk = Keys.sharedKeyBuilder().sharedBy(sharedBy).sharedWith(sharedWith).name(KEY_NAME).build(); // 5. get the key String response = atClient.get(sk).get(); diff --git a/lombok.config b/lombok.config new file mode 100644 index 00000000..7a21e880 --- /dev/null +++ b/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation = true diff --git a/pom.xml b/pom.xml index faba552b..9d3f6311 100644 --- a/pom.xml +++ b/pom.xml @@ -230,6 +230,9 @@ + + true +