diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cbadb298..53f3520b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,11 +16,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Set up JDK 8 and maven cache + - name: Set up JDK 11 and maven cache uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: - java-version: '8' - distribution: 'adopt' + java-version: '11' + distribution: 'temurin' cache: maven - name: Add virtual env entry to hosts file run: echo "127.0.0.1 vip.ve.atsign.zone" | sudo tee -a /etc/hosts diff --git a/.github/workflows/maven-deploy.yml b/.github/workflows/maven-deploy.yml index 9e937ba4..6d56c844 100644 --- a/.github/workflows/maven-deploy.yml +++ b/.github/workflows/maven-deploy.yml @@ -16,11 +16,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: - java-version: '8' - distribution: 'adopt' + java-version: '11' + distribution: 'temurin' server-id: central # Value of the distributionManagement/repository/id field of the pom.xml server-username: "CENTRAL_USERNAME" # env variable for username in deploy server-password: "CENTRAL_TOKEN" # env variable for token in deploy diff --git a/README.md b/README.md index 284246de..7deae505 100644 --- a/README.md +++ b/README.md @@ -8,161 +8,132 @@ [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/atsign-foundation/at_java/badge)](https://securityscorecards.dev/viewer/?uri=github.com/atsign-foundation/at_java&sort_by=check-score&sort_direction=desc) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8116/badge)](https://www.bestpractices.dev/projects/8116) -## The atPlatform for Java developers - -This repo contains libraries, tools, samples and examples for developers who wish -to work with the atPlatform from Java code. - -## Maven Dependency - -The Java SDK can be added to your project through a compiled JAR or by Maven! - -```xml - - - Central Portal Snapshots - central-portal-snapshots - https://central.sonatype.com/repository/maven-snapshots - - false - - - true - - - - - - - org.atsign - at_client - 0.0.1-SNAPSHOT - - -``` +## The Atsign Platform for Java developers -## Getting Started +This repository contains libraries, tools, samples and examples for developers that +wish to work with the Atsign Platform from Java code. -### Prerequisites +## Modules -* Java (JDK 8) -* Maven +There are 4 modules. -### Steps +1. **at_client** is the Java SDK for interacting with the Atsign Platform + [README](at_client/README.md). +2. **at_shell** is a self-contained "fatjar" that provides an interactive command + line interface for performing various operations and tests + [README](at_shell/README.md). +3. **at_utils** contains useful code which is not part of the SDK + [README](at_utils/README.md). +4. **examples** contains sample code which illustrates how to use the SDK + [README](examples/README.md). -Clone the at_java repo from GitHub using +## Build Process -```shell -git clone https://github.com/atsign-foundation/at_java.git -``` +### Prerequisites -Change directory into at_java/at_client +* Java (JDK 11 or above) +* Maven (3.6.3 or above) -```shell -cd at_client -``` - -Compile the package using maven with the following command +Check out, build and test with the following commands: -```shell +```text +git clone https://github.com/atsign-foundation/at_java.git mvn install ``` -Now that the programs have been compiled, execute the following command to use at_java +**Note:** The integration tests rely on the virtual env. The tests +will attempt to start a docker container with this image but that relies +on dockerd (or desktop docker). If you want to skip these tests then add +**-DskipITs**. See subsequent section for more information. -```shell -java -cp "target/at_client-1.0-SNAPSHOT.jar:target/lib/*" \ -org.atsign.client.cli. [required arguments] +## Unit Tests + +These have no external dependencies and are run as part of the maven **test** +lifecycle. The surefire plugin will pick up classes that end in the following: + +```text +**/*Test.java +**/*Tests.java +**/*TestCase.java ``` -## Main Classes +Use **-DskipTests** to avoid running unit tests. + +### Integration Tests -1) REPL -2) Share -3) Get -4) Delete -5) Register -6) Activate +These depend on running the atsign virtual environment and are run as part +of the maven **verify** lifecycle. The failsafe plugin will pick up classes that +end in the following: -**Note:** Each of these classes requires a different set of arguments, make -sure to read the help text and provide necessary arguments. +```text +**/*IT.java +``` -### Register +Use **-DskipITs** to avoid running integration tests. -A class that accepts command line arguments which are used to fetch a free -atsign and register it to the email provided. Further, this atsign can be -activated using a verification code sent to the registered email. +The integration tests check to see if a virtual env is running. If not they will +attempt to start one. This requires dockerd or desktop docker to be running. -To run use the following command: +For CI, "standing up" and then "tearing down" the docker container is the intended +behavior. However, for a developer this will be slow so it's preferable to "standup" +the virtual env independently, like this: ```shell -java -cp "target/at_client-1.0-SNAPSHOT.jar:target/lib/*" \ -org.atsign.client.cli.Register -e email@example.com +cd at_client/src/test/resources/org/atsign/virtualenv +docker compose -f up ``` -### Register with SUPER_API Key +The start-up can take a few minutes and involves running scripts that install the +test configuration. The environment is ready to test with when you see the +following log output: -Register can also be used with a SUPER_API Key that has privileges to preset -and atsign with an activation code. +```text +... +virtualenv-1 | SHOUT|...| install_PKAM_Keys |cramAndPkamAuth successful for @chris +virtualenv-1 | SHOUT|...| install_PKAM_Keys |cramAndPkamAuth successful for @policy1 +virtualenv-1 | SHOUT|...| install_PKAM_Keys |cramAndPkamAuth successful for @emoji +``` + +The tests require test keys (CRAM keys and atKeys) which the virtual env was +built with. The pom contains a plugin which downloads the +[at_demo_data](https://pub.dev/packages/at_demo_data) package and unpacks it +under target. When the tests run this is where they expect to find keys. The +release version is specified as property in the POM and needs to be periodically +updated to the latest release. -To run use the following command: +**Note:** The integration tests are designed to reset the virtual env as best as +possible. This relies on teardown steps. If you are debugging tests and terminate +the execution then this can leave the virtual env in an unreset state. Running +the test again might fail because of this BUT that teardown should successfully +reset the env and allow the next run to succeed. In extreme circumstances it may +be necessary to reset the docker container, like this: ```shell -java -cp "target/at_client-1.0-SNAPSHOT.jar:target/lib/*" \ -org.atsign.client.cli.Register -k +cd at_client/src/test/resources/org/atsign/virtualenv +docker compose -f down +docker compose -f up ``` -When using the SUPER_API Key to register an atsign, the following sequence of -calls take place: -1) User provides at_java/Register with the SUPER_API Key passed as an argument -2) at_java calls the AtSign Registrar API* Endpoint(get-atsign) with the -SUPER_API Key provided -3) The AtSign registrar API responds with an AtSign-ActivationKey pair -4) at_java now call the AtSign Registrar API* Endpoint(activate-atsign) with -the AtSign-ActivationKey pair -5) The API responds with a json containing the CRAM_KEY* for the concerned -atsign -6) This CRAM_KEY* can be used to activate the atsign further making it usable -7) at_java does the activation automatically for you and stores your atKeys* -file at path '~/.atsign/keys' -8) Now the atsign is activated and the atKeys file can be used to -authenticate and perform protected operation with/on the atSign. - -### Things to know about at_platform - -1) Register: This is a class in at_java that has the functionality to call -the necessary API, handle responses in order to fetch and register atsigns. -2) AtSign Registrar API: An AtSign service that is responsible for handling -atsign's server creation, registration, authentication, reset and deletion. -3) SUPER_API Key - * All calls to the AtSign Registrar API require an API_KEY. But the - SUPER_API Key has some additional privileges. - * SUPER_API Keys have the privilege to preset an AtSign with an activation - key so that this AtSign can be activated without manually entering a - verification code that is sent to the registered email. - * All SUPER_API Keys have a name containing two elements [say pre and - post], all the atsigns generated using this API_Key will be of the - following format: (pre)atsign(post). Now the atsign will be @preatsignpost. - This is done to separate atsigns generated using SUPER_API Keys to the - atsigns that are generated through other methods. -4) CRAM_KEY: This is an authentication key that will be used for a one-time -authentication to activate an atsign which allows for assigning random, -secure non-symmetric keypairs which will be further stored in the users -atKeys file. - * Note: CRAM_KEY will be deleted from the atsign server after an atKeys - file has been generated, so only you have the keys to authenticate into - your atsign. -5) atKeys file: This will be a file generated during activation of an atsign -that stores all the keys necessary for authenticating into atSign - * That would mean users have to keep this file in a secured location - * Users should keep this file safe, as there's only one copy of this file - and losing it would mean the user would be unable to log in to the atsign. - * If lost, users can reset the atsign and get a new atKeys file. This - would result in loss of all data stored in the atsign's server. - -### Code Style And Formatting - -The maven pom contains the following plugins to enforce a consistent coding +## Virtual Environment + +This is a docker image that bundles the following: + +* redis +* root server +* multiple at_servers in different states of configuration + +The docker image is published as part of the +[at_server](https://github.com/atsign-foundation/at_server) repository. + +The keys (CRAM secrets, pre-cut AtKeys) are part the +[at_demos](https://github.com/atsign-foundation/at_demos) repository. + +The pom.xml for modules which run have integration tests include a plugin +which downloads the at_demos package to target. + +## Code Style And Formatting + +The maven poms contain the following plugins to enforce a consistent coding style and format. * [checkstyle](https://checkstyle.org) @@ -170,8 +141,8 @@ style and format. The rules which configure the respective plugins are here -* [config/checkstyle.xml](config/checkstyle.xml) -* [config/java-format.xml](config/java-format.xml) +* [checkstyle.xml](config/checkstyle.xml) +* [java-format.xml](config/java-format.xml) Run the following maven command run the checks: @@ -185,17 +156,17 @@ Run the following maven command to fix the spotless violations: mvn spotless:apply ``` -#### Intellij +### Intellij To configure Intellij to use the same settings -1. Add the **Adaptor for Eclopse Code Formatter** plugin and configure in +1. Add the **Adaptor for Eclipse Code Formatter** plugin and configure in **Settings -> Adaptor for Eclipse Code Formatter** by setting **Eclipse workspace/project folder or config file** as config/java-format.xml 2. Add **CheckStyle-IDEA** plugin and configure in **Settings -> Tools -> Checkstyle** by adding config/checkstyle.xml -### Contributions welcome +## Contributions welcome All of our software is open with intent. We welcome contributions - we want pull requests, and we want to hear about issues. See also diff --git a/at_client/README.md b/at_client/README.md index 2adbc310..1a5994a0 100644 --- a/at_client/README.md +++ b/at_client/README.md @@ -1,7 +1,32 @@ -# AtSign Java SDK +# Java Client SDK + This module contains the AtSign Java SDK. -Add the following Maven configuration to your pom in order to use the latest version of this library. +## Using the SDK + +If you are using maven, add the following to your pom.xml + +```xml + + + org.atsign + at_client + 1.0.0 + + +``` + +If you are using gradle, add the following to your build.gradle + +```text +dependencies { + implementation 'org.atsign:at_client:1.0.0' +} +``` + +### Snapshot Version + +The latest snapshot version can be added as a maven dependency like this... ```xml @@ -22,96 +47,163 @@ Add the following Maven configuration to your pom in order to use the latest ver org.atsign at_client - 0.0.1-SNAPSHOT + 1.0.1-SNAPSHOT ``` -## Dependencies +## Logging -The library currently depends on the following +The SDK uses the slf4j-api. It is up to you to decide which slf4j binding to use. +See [slf4j providers](https://slf4j.org/manual.html#swapping). -* Bouncycastle -* Jackson -* Pico CLI +## Dependencies -## Developer Instructions +The SDK currently depends on the following -The following command will compile, run unit tests and integration tests +* Bouncycastle (encryption, decryption, and cryptography utilities) +* Jackson (support for JSON and YAML encoding and decoding) +* Pico CLI (lightweight command line interface framework) +* Slf4j api (Simple logging facade that can be bound a variety of logging + frameworks) -```shell -mvn clean install -``` +## Example Usage -### Unit Tests +The command line classes under +[src/main/java/org/atsign/client/cli](src/main/java/org/atsign/client/cli) +serve as simple examples of how to instantiate an AtClient instance and invoke its +interface. -These have no external dependencies. +See also the [examples](../examples) module. -The following command will compile and run these +--- -```shell -mvn clean test -``` +## Command Line Utilities -The surefire plugin should pick up classes that end in the following +The following utilities provide a simple way to test behavior as-well as illustrating +the fundamentals of using the SDK. -```text -**/*Test.java -**/*Tests.java -**/*TestCase.java -``` +**Note:** The example command lines use the **exec-maven-plugin** as this will handle +setting the classpath (see Dependencies section above). -### Integration Tests +**Note:** The example command lines which include vip.ve.atsign.zone:64 assume that +you are running the virtual environment (see section below). -These depend on running the at virtual environment. The integration tests check to see if a virtual env is running. -If not they will attempt to start one. This requires dockerd or desktop docker to be running. For CI standing up -and then tearing down the docker container is the intended behavior. For a developer this can be expensive so it's -preferable to "standup" the virtual env independently. +### Share + +The utility **org.atsign.client.cli.Share** can be used to share a value with +another AtSign. In this case, **@gary** is sharing the key value pair +(**message**, **hello**) with **@colin**. ```shell -cd src/test/resources/org/atsign/virtualenv -docker compose up +mvn exec:java -Dexec.mainClass=org.atsign.client.cli.Share \ + -Dexec.args="vip.ve.atsign.zone:64 @gary @colin message hello" ``` -The start up can take a few minutes and involves running scripts that install test configuration. The environment -is ready to test with when you see the following log output +### Get -```text -... -virtualenv-1 | SHOUT|2026-02-04 07:16:40.933352| install_PKAM_Keys |cramAndPkamAuth successful for @chris -virtualenv-1 | SHOUT|2026-02-04 07:16:40.933750| install_PKAM_Keys |cramAndPkamAuth successful for @policy1 -virtualenv-1 | SHOUT|2026-02-04 07:16:40.934626| install_PKAM_Keys |cramAndPkamAuth successful for @emoji +The utility **org.atsign.client.cli.Get** can be used to get a value that has +been shared by another AtSign. In this case **@colin** is getting the value +for the key **message** which has been shared by **@gary**. + +```shell +mvn exec:java -Dexec.mainClass=org.atsign.client.cli.Get \ + -Dexec.args="vip.ve.atsign.zone:64 @colin @gary message" ``` -The following command will compile, run unit tests and then run the integration tests +### Scan + +The utility **org.atsign.client.cli.Scan** can be used to list keys that exist +at the AtServer of an AtSign. In this case **@gary** is listing the keys at his +own AtServer. ```shell -mvn clean verify +mvn exec:java -Dexec.mainClass=org.atsign.client.cli.Scan \ + -Dexec.args="vip.ve.atsign.zone:64 @gary .*" ``` -The tests require test keys (CRAM keys and atKeys) which the virtual env was built with. The pom contains a plugin -which downloads the at_demo_data package [https://pub.dev/packages/at_demo_data](https://pub.dev/packages/at_demo_data) -and unpacks it under target. The release version is specified as property in the POM and needs to be periodically -updated to the latest release. +### Delete -The failsafe plugin should pick up classes that end in the following +The utility **org.atsign.client.cli.Delete** can be used to delete a value that +was previously shared. In this case, **@gary** is deleting the key value for +**message** that was previously shared with **@colin**. -```text -**/*IT.java +```shell +mvn exec:java -Dexec.mainClass=org.atsign.client.cli.Delete \ + -Dexec.args="vip.ve.atsign.zone:64 @gary @colin message" ``` -### Virtual Environment +### Register -This is a docker image that bundles the following. -* redis -* root server -* multiple pre-configured at servers +The utility **org.atsign.client.cli.Register** can be used to perform +registration operations. -This docker image is built as part of this repo +```shell +mvn exec:java -Dexec.mainClass=org.atsign.client.cli.Register -Dexec.args="--help" +``` -[https://github.com/atsign-foundation/at_server](https://github.com/atsign-foundation/at_server) +When using the SUPER_API Key to register an atsign, the following sequence of +calls take place: +1. User provides at_java/Register with the SUPER_API Key passed as an argument +2. at_java calls the AtSign Registrar API* Endpoint(get-atsign) with the + SUPER_API Key provided +3. The AtSign registrar API responds with an AtSign-ActivationKey pair +4. at_java now call the AtSign Registrar API* Endpoint(activate-atsign) with + the AtSign-ActivationKey pair +5. The API responds with a json containing the CRAM_KEY* for the concerned + atsign +6. This CRAM_KEY* can be used to activate the atsign further making it usable +7. at_java does the activation automatically for you and stores your atKeys* + file at path '~/.atsign/keys' +8. Now the atsign is activated and the atKeys file can be used to + authenticate and perform protected operation with/on the atSign. + +#### Things to know about at_platform + +1. Register: This is a class in at_java that has the functionality to call + the necessary API, handle responses in order to fetch and register atsigns. +2. AtSign Registrar API: An AtSign service that is responsible for handling + atsign's server creation, registration, authentication, reset and deletion. +3. SUPER_API Key + All calls to the AtSign Registrar API require an API_KEY. But the + SUPER_API Key has some additional privileges. + SUPER_API Keys have the privilege to preset an AtSign with an activation + key so that this AtSign can be activated without manually entering a + verification code that is sent to the registered email. + All SUPER_API Keys have a name containing two elements [say pre and + post], all the atsigns generated using this API_Key will be of the + following format: (pre)atsign(post). Now the atsign will be @preatsignpost. + This is done to separate atsigns generated using SUPER_API Keys to the + atsigns that are generated through other methods. +4. CRAM_KEY: This is an authentication key that will be used for a one-time + authentication to activate an atsign which allows for assigning random, + secure non-symmetric keypairs which will be further stored in the users + atKeys file. **Note:** CRAM_KEY will be deleted from the atsign server after + an atKeys file has been generated, so only you have the keys to authenticate + into your atsign. +5. atKeys file: This will be a file generated during activation of an atsign + that stores all the keys necessary for authenticating into atSign + That would mean users have to keep this file in a secured location + Users should keep this file safe, as there's only one copy of this file + and losing it would mean the user would be unable to log in to the atsign. + If lost, users can reset the atsign and get a new atKeys file. This + would result in loss of all data stored in the atsign's server. + +### Activate + +The utility **org.atsign.client.cli.Activate** can be used to perform +onboarding and enrollment operations. Running using the **exec-maven-plugin** +will handle the classpath dependencies. -The keys (CRAM secrets, pre-cut AtKeys) are part of this repo +```shell +mvn exec:java -Dexec.mainClass=org.atsign.client.cli.Activate -Dexec.args="--help" +``` + +### DumpKeys -[https://github.com/atsign-foundation/at_demos](https://github.com/atsign-foundation/at_demos) +The utility **org.atsign.client.cli.DumpKeys** can be used to dumps the contents +of an AtSigns AtKeys. In this case **@gary**. +```shell +mvn exec:java -Dexec.mainClass=org.atsign.client.cli.DumpKeys -Dexec.args="@gary" +``` diff --git a/at_client/pom.xml b/at_client/pom.xml index 5de4bfca..9c78aa5e 100644 --- a/at_client/pom.xml +++ b/at_client/pom.xml @@ -82,6 +82,14 @@ maven-checkstyle-plugin + + org.codehaus.mojo + exec-maven-plugin + + test + + + 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 db261fea..1dcaf01e 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,24 +1,20 @@ package org.atsign.client.cli; -import java.util.concurrent.ExecutionException; - +import lombok.extern.slf4j.Slf4j; import org.atsign.client.api.AtClient; -import org.atsign.client.util.ArgsUtil; +import org.atsign.client.api.AtKeys; import org.atsign.client.util.KeysUtil; -import org.atsign.common.AtException; import org.atsign.common.AtSign; import org.atsign.common.KeyBuilders; +import org.atsign.common.Keys; /** * A command-line interface half-example half-utility to delete something that was previously shared */ +@Slf4j public class Delete { - @SuppressWarnings("DuplicatedCode") - public static void main(String[] args) { - String rootUrl; // e.g. "root.atsign.org:64"; - AtSign atSign; // e.g. "@alice"; - AtSign otherAtSign; // e.g. "@bob"; - String keyName; + + public static void main(String[] args) throws Exception { if (args.length != 4) { System.err @@ -26,28 +22,23 @@ public static void main(String[] args) { System.exit(1); } - rootUrl = args[0]; - atSign = new AtSign(args[1]); - otherAtSign = new AtSign(args[2]); - keyName = args[3]; - - AtClient atClient = null; - try { - atClient = AtClient.withRemoteSecondary(atSign, KeysUtil.loadKeys(atSign), ArgsUtil.createAddressFinder(rootUrl)); - } catch (AtException e) { - System.err.println("Failed to create AtClientImpl : " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } + String rootUrl = args[0]; + AtSign atSign = new AtSign(args[1]); + AtSign otherAtSign = new AtSign(args[2]); + String keyName = args[3]; - try { - String deleteResponse = - atClient.delete(new KeyBuilders.SharedKeyBuilder(atSign, otherAtSign).key(keyName).build()).get(); - System.out.println("delete response : " + deleteResponse); - } catch (InterruptedException | ExecutionException e) { - System.err.println("Failed to get : " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); + // all AtClients require AtKeys, this loads them based on the AtSign from the default location + AtKeys keys = KeysUtil.loadKeys(atSign); + + try (AtClient atClient = AtClient.withRemoteSecondary(rootUrl, atSign, keys, true)) { + + Keys.SharedKey key = new KeyBuilders.SharedKeyBuilder(atSign, otherAtSign) + .key(keyName) + .build(); + + String response = atClient.delete(key).get(); + + log.info("delete response : {}", response); } } } diff --git a/at_client/src/main/java/org/atsign/client/cli/DumpKeys.java b/at_client/src/main/java/org/atsign/client/cli/DumpKeys.java index 1982ed1b..bb5edb01 100644 --- a/at_client/src/main/java/org/atsign/client/cli/DumpKeys.java +++ b/at_client/src/main/java/org/atsign/client/cli/DumpKeys.java @@ -1,10 +1,15 @@ package org.atsign.client.cli; +import lombok.extern.slf4j.Slf4j; +import org.atsign.client.api.AtKeys; import org.atsign.client.util.KeysUtil; import org.atsign.common.AtSign; +@Slf4j public class DumpKeys { public static void main(String[] args) throws Exception { - System.out.println(KeysUtil.dump(KeysUtil.loadKeys(new AtSign(args[0])))); + AtSign atSign = new AtSign(args[0]); + AtKeys keys = KeysUtil.loadKeys(atSign); + System.out.println(KeysUtil.dump(keys)); } } 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 3143b1c1..90d17994 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,11 +1,9 @@ package org.atsign.client.cli; -import java.util.concurrent.ExecutionException; - +import lombok.extern.slf4j.Slf4j; import org.atsign.client.api.AtClient; -import org.atsign.client.util.ArgsUtil; +import org.atsign.client.api.AtKeys; import org.atsign.client.util.KeysUtil; -import org.atsign.common.AtException; import org.atsign.common.AtSign; import org.atsign.common.KeyBuilders; import org.atsign.common.Keys; @@ -14,41 +12,33 @@ * A command-line interface half-example half-utility to get something that was shared by another * atSign */ +@Slf4j public class Get { - public static void main(String[] args) { - String rootUrl; // e.g. "root.atsign.org:64"; - AtSign atSign; // e.g. "@alice"; - AtSign otherAtSign; // e.g. "@bob"; - String keyName; + + public static void main(String[] args) throws Exception { if (args.length != 4) { System.err.println("Usage: Get "); System.exit(1); } - //noinspection DuplicatedCode - rootUrl = args[0]; - atSign = new AtSign(args[1]); - otherAtSign = new AtSign(args[2]); - keyName = args[3]; - - AtClient atClient = null; - try { - atClient = AtClient.withRemoteSecondary(atSign, KeysUtil.loadKeys(atSign), ArgsUtil.createAddressFinder(rootUrl)); - } catch (AtException e) { - System.err.println("Failed to create AtClientImpl : " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } + String rootUrl = args[0]; + AtSign atSign = new AtSign(args[1]); + AtSign otherAtSign = new AtSign(args[2]); + String keyName = args[3]; - try { - Keys.SharedKey sharedKey = new KeyBuilders.SharedKeyBuilder(otherAtSign, atSign).key(keyName).build(); - String getResponse = atClient.get(sharedKey).get(); - System.out.println("get response : " + getResponse); - } catch (InterruptedException | ExecutionException e) { - System.err.println("Failed to get : " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); + // all AtClients require AtKeys, this loads them based on the AtSign from the default location + AtKeys keys = KeysUtil.loadKeys(atSign); + + try (AtClient atClient = AtClient.withRemoteSecondary(rootUrl, atSign, keys, true)) { + + Keys.SharedKey key = new KeyBuilders.SharedKeyBuilder(otherAtSign, atSign) + .key(keyName) + .build(); + + String response = atClient.get(key).get(); + + log.info("get response : {}", response); } } } diff --git a/at_client/src/main/java/org/atsign/client/cli/Register.java b/at_client/src/main/java/org/atsign/client/cli/Register.java index 70298c13..f2f7974c 100644 --- a/at_client/src/main/java/org/atsign/client/cli/Register.java +++ b/at_client/src/main/java/org/atsign/client/cli/Register.java @@ -53,6 +53,7 @@ public String call() throws Exception { } String[] onboardArgs = new String[] { + "onboard", "-r", params.get("rootDomain") + ":" + params.get("rootPort"), "-a", params.get("atSign"), "-c", params.get("cram")}; 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 b20b56f1..7ea83033 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 @@ -1,145 +1,107 @@ package org.atsign.client.cli; -import java.io.IOException; +import java.io.PrintStream; import java.util.List; import java.util.Scanner; -import java.util.concurrent.ExecutionException; import org.atsign.client.api.AtClient; -import org.atsign.client.api.Secondary; -import org.atsign.client.util.ArgsUtil; +import org.atsign.client.api.AtKeys; import org.atsign.client.util.KeysUtil; import org.atsign.client.util.StringUtil; -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.AtSecondaryNotFoundException; + +import lombok.extern.slf4j.Slf4j; /** * A command-line interface for scanning keys in your secondary (must have keys to atSign in keys/) */ +@Slf4j public class Scan { - public static void main(String[] args) { + public static void main(String[] args) throws Exception { - if (args.length != 4) { - System.out.println("Incorrect usage | Scan "); - return; + if (args.length != 3) { + System.out.println("Incorrect usage | Scan "); + System.exit(1); } - // fetch command line args - String rootUrl = args[0]; // root.atsign.wtf:64 - String atSignConst = args[1]; // @sportsunconscious - String verboseStr = args[2]; // true|false (true for noisy print logs) - String regex = args[3]; // scan regex (e.g. ".*") - - // ====================================================== - AtSign atSign = new AtSign(atSignConst); - boolean verbose = Boolean.parseBoolean(verboseStr); - String what = null; // error message string + String rootUrl = args[0]; + AtSign atSign = new AtSign(args[1]); + String regex = args[2]; - // find secondary address - Secondary.Address sAddress = null; - try { - Secondary.AddressFinder sAddressFinder = ArgsUtil.createAddressFinder(rootUrl); - what = "find secondary with atSign:" + atSign.atSign; - sAddress = sAddressFinder.findSecondary(atSign); - System.out.println("Found address of atSign \"" + atSign.atSign + "\": " + sAddress.host + ":" + sAddress.port); - } catch (IOException | AtSecondaryNotFoundException e) { - System.err.println("Failed to " + what + " " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } + // all AtClients require AtKeys, this loads them based on the AtSign from the default location + AtKeys keys = KeysUtil.loadKeys(atSign); - // initialize AtClient and connect to remote secondary (with pkam auth therefore must have keys to secondary) - AtClient atClient = null; - try { - what = "initialize AtClient"; - atClient = AtClient.withRemoteSecondary(atSign, KeysUtil.loadKeys(atSign), sAddress, verbose); - } catch (AtException e) { - System.err.println("Failed to " + what + " " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } + try (AtClient atClient = AtClient.withRemoteSecondary(rootUrl, atSign, keys, true)) { - // run scan - List atKeys = null; - try { - what = "getAtKeys(" + regex + ")"; - atKeys = atClient.getAtKeys(regex).get(); - } catch (InterruptedException | ExecutionException e) { - System.err.println("Failed to " + what + " " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } + List response = atClient.getAtKeys(regex).get(); - // CLI - String input; - Scanner scanner = new Scanner(System.in); - do { - System.out.println(); - // _printAtKeys(atKeys); - System.out.println("Enter index you want to llookup (l to list, q to quit):"); - input = scanner.nextLine(); - if (StringUtil.isNumeric(input)) { - int index = Integer.parseInt(input); - if (index < atKeys.size()) { - AtKey atKey = atKeys.get(index); - _printAtKeyInfo(atKey); - } else { - System.out.println("Index out of bounds"); + String input; + Scanner scanner = new Scanner(System.in); + do { + System.out.println(); + System.out.println("Enter index you want to llookup (l to list, q to quit):"); + input = scanner.nextLine(); + if (StringUtil.isNumeric(input)) { + int index = Integer.parseInt(input); + if (index < response.size()) { + printKeyInfo(response.get(index), System.out); + } else { + System.out.println("Index out of bounds"); + } + } else if ("l".equalsIgnoreCase(input)) { + printKeys(response, System.out); + } else if (!"q".equalsIgnoreCase(input)) { + System.out.println("Invalid input"); } - } else if ("l".equalsIgnoreCase(input)) { - _printAtKeys(atKeys); - } else if (!"q".equalsIgnoreCase(input)) { - System.out.println("Invalid input"); - } - } while (!"q".equalsIgnoreCase(input)); - scanner.close(); + } while (!"q".equalsIgnoreCase(input)); + scanner.close(); + } } - private static void _printAtKeys(List atKeys) { - System.out.println("atKeys: {"); - for (int i = 0; i < atKeys.size(); i++) { - AtKey atKey = atKeys.get(i); - System.out.println(" " + i + ": " + (atKey.metadata.isCached ? "cached:" : "") + atKey); + 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); } - System.out.println("}"); + out.println("}"); } - private static void _printAtKeyInfo(AtKey atKey) { - System.out.println("======================"); - System.out.println("Full KeyName: " + atKey.toString()); - System.out.println("KeyName: " + atKey.name); - System.out.println("Namespace: " + atKey.getNamespace()); - System.out.println("SharedBy: " + atKey.sharedBy.atSign); - System.out.println("SharedWith: " + (atKey.sharedWith != null ? atKey.sharedWith.atSign : "null")); - System.out.println("KeyType: " + atKey.getClass().toString().split("\\$")[1]); - System.out.println("Metadata -------------------"); - _printMetadata(atKey.metadata); - System.out.println("======================"); - System.out.println(); + 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("KeyType: " + key.getClass().toString().split("\\$")[1]); + out.println("Metadata -------------------"); + printKeyMetadata(key.metadata, out); + out.println("======================"); + out.println(); } - 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); + 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); } } 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 559ea5b8..a95aed67 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 @@ -1,75 +1,47 @@ package org.atsign.client.cli; -import static org.atsign.common.KeyBuilders.SharedKeyBuilder; - -import java.time.OffsetDateTime; -import java.util.concurrent.ExecutionException; - import org.atsign.client.api.AtClient; -import org.atsign.client.api.Secondary; -import org.atsign.client.util.ArgsUtil; +import org.atsign.client.api.AtKeys; import org.atsign.client.util.KeysUtil; -import org.atsign.common.AtException; 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 share something with another atSign */ +@Slf4j public class Share { - public static void main(String[] args) { - String rootUrl; // e.g. "root.atsign.org:64"; - AtSign atSign; // e.g. "@alice"; - AtSign otherAtSign; // e.g. "@bob"; - String keyName; - String toShare; - int ttr; + public static void main(String[] args) throws Exception { - if (args.length != 6) { - System.err - .println("Usage: Share "); + if (args.length < 5) { + System.err.println("Usage: Share " + + " "); System.exit(1); } - rootUrl = args[0]; - atSign = new AtSign(args[1]); - otherAtSign = new AtSign(args[2]); - keyName = args[3]; - toShare = args[4]; - ttr = Integer.parseInt(args[5]); + String rootUrl = args[0]; + AtSign atSign = new AtSign(args[1]); + AtSign otherAtSign = new AtSign(args[2]); + String keyName = args[3]; + String toShare = args[4]; + int ttr = args.length == 6 ? Integer.parseInt(args[5]) : 0; - Secondary.AddressFinder addressFinder = ArgsUtil.createAddressFinder(rootUrl); - // Let's also look up the other one before we do anything, just in case - try { - addressFinder.findSecondary(otherAtSign); - } catch (Exception e) { - System.err.println("Failed to look up remote secondary for " + otherAtSign + " : " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } + // all AtClients require AtKeys, this loads them based on the AtSign from the default location + AtKeys keys = KeysUtil.loadKeys(atSign); - AtClient atClient = null; - try { - atClient = AtClient.withRemoteSecondary(atSign, KeysUtil.loadKeys(atSign), addressFinder); - } catch (AtException e) { - System.err.println("Failed to create AtClientImpl : " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); - } + try (AtClient atClient = AtClient.withRemoteSecondary(rootUrl, atSign, keys, true)) { - try { - SharedKeyBuilder sharedKeyBuilder = new SharedKeyBuilder(atSign, otherAtSign) + Keys.SharedKey key = new KeyBuilders.SharedKeyBuilder(atSign, otherAtSign) + .key(keyName) .cache(ttr, true) - .key(keyName); - Keys.SharedKey sharedKey = sharedKeyBuilder.build(); + .build(); - System.out.println(OffsetDateTime.now() + " | calling atClient.put()"); - String putResponse = atClient.put(sharedKey, toShare).get(); - System.out.println(OffsetDateTime.now() + " | put response : " + putResponse); - } catch (InterruptedException | ExecutionException e) { - System.err.println("Failed to share : " + e.getMessage()); - e.printStackTrace(System.err); - System.exit(1); + String response = atClient.put(key, toShare).get(); + + log.info("put response : {}", response); } } diff --git a/at_client/src/main/java/org/atsign/client/util/StringUtil.java b/at_client/src/main/java/org/atsign/client/util/StringUtil.java index 1bef3637..5b78a3de 100644 --- a/at_client/src/main/java/org/atsign/client/util/StringUtil.java +++ b/at_client/src/main/java/org/atsign/client/util/StringUtil.java @@ -1,11 +1,9 @@ package org.atsign.client.util; -// enables removal of commons-lang -// TODO: review after JDK upgrade public class StringUtil { public static boolean isBlank(String s) { - return s == null || s.trim().isEmpty(); + return s == null || s.isBlank(); } public static boolean isNumeric(String s) { diff --git a/at_client/src/main/java/org/atsign/config/ConfigReader.java b/at_client/src/main/java/org/atsign/config/ConfigReader.java index 8942558d..c36f63fa 100644 --- a/at_client/src/main/java/org/atsign/config/ConfigReader.java +++ b/at_client/src/main/java/org/atsign/config/ConfigReader.java @@ -37,7 +37,7 @@ public static String getProperty(String property) throws IOException { * key-value pairs in the config map */ public static void loadConfig() throws IOException { - InputStream inputStream = ClassLoader.getSystemResourceAsStream("config.yaml"); + InputStream inputStream = ConfigReader.class.getClassLoader().getResourceAsStream("config.yaml"); // noinspection unchecked config = mapper.readValue(inputStream, HashMap.class); } diff --git a/at_shell/README.md b/at_shell/README.md index fdbffdc8..d894cff0 100644 --- a/at_shell/README.md +++ b/at_shell/README.md @@ -1,15 +1,34 @@ -# at_shell +# Java Shell Interactive command line utility which uses the Java at_client library -## Building +## Usage + +To see usage: ```shell -mvn clean package +java -jar target/at_shell-0.0.1-SNAPSHOT.jar ``` -## Usage +To run shell against virtual env as @gary: ```shell -java -jar target/at_shell-0.0.1-SNAPSHOT.jar +java -jar target/at_shell-0.0.1-SNAPSHOT.jar vip.ve.atsign.zone:64 @gary ``` + +**Note:** This will default to looking in $HOME/.atsign for AtKeys. + +To point to the at_demo_data package keys: + +```shell +java -DATSIGN_KEYS_DIR=target/at_demo_data/lib/assets/atkeys \ + -DATSIGN_KEYS_SUFFIX=.atKeys \ + -jar target/at_shell-0.0.1-SNAPSHOT.jar vip.ve.atsign.zone:64 @gary +``` + +**Note:** JDK 9+ has stricter JAR verification for cryptographic providers. +This means that BouncyCastle cannot be included in the shaded jar (this would +remove the signature). The maven build downloads the bouncycastle jar to +target/lib and include this in the jar manifest classpath. If you copy the +FAT jar elsewhere you need to ensure that lib dir, containing the bouncycastle +jar is also copied too. diff --git a/at_shell/pom.xml b/at_shell/pom.xml index 14893c6f..bcabeb65 100644 --- a/at_shell/pom.xml +++ b/at_shell/pom.xml @@ -98,10 +98,19 @@ META-INF/*.RSA + + org.bouncycastle:* + + ** + + org.atsign.client.cli.REPL + + lib/bcprov-jdk15to18-${version.bouncycastle}.jar + @@ -109,6 +118,45 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/lib + runtime + org.bouncycastle + + + + + + + + com.googlecode.maven-download-plugin + download-maven-plugin + + + download-at_demo_data + generate-test-resources + + wget + + + https://pub.dev/packages/at_demo_data/versions/${version.at_demo_data}.tar.gz + ${project.build.directory}/at_demo_data + true + + + + + 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 fd776baf..0e066e5c 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 @@ -35,22 +35,16 @@ public class REPL { public static void main(String[] args) { AnsiConsole.systemInstall(); - String rootUrl; // e.g. "root.atsign.org:64"; - AtSign atSign; // e.g. "@alice"; - boolean verbose = false; - - if (args.length < 3) { + if (args.length != 2 && args.length != 4) { System.err - .println("Usage: REPL []"); + .println("Usage: REPL [seeEncryptedNotifications == 'true|false'] []"); System.exit(1); } - rootUrl = args[0]; - atSign = new AtSign(args[1]); - boolean seeEncryptedNotifications = Boolean.parseBoolean(args[2]); - if (args.length >= 4) { - verbose = Boolean.parseBoolean(args[3]); - } + String rootUrl = args[0]; + AtSign atSign = new AtSign(args[1]); + boolean seeEncryptedNotifications = args.length > 2 ? Boolean.parseBoolean(args[2]) : false; + boolean verbose = args.length > 3 ? Boolean.parseBoolean(args[3]) : false; AtClient atClient; try { diff --git a/at_utils/README.md b/at_utils/README.md index 7c77d4e5..266486eb 100644 --- a/at_utils/README.md +++ b/at_utils/README.md @@ -1 +1,3 @@ -Additional Java utilities \ No newline at end of file +# Java Utilities + +Miscellaneous code that is useful but not appropriate to bundle into the SDK. diff --git a/examples/README.md b/examples/README.md index 6afd8112..cd219af1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1 +1,9 @@ -Java at_client usage examples \ No newline at end of file +# Java Client SDK Examples + +It is intended to illustrate the following: + +* Instantiating an AtClient instance +* Invoke the API the put/get/delete "map" like interface for the different + key types (Public Keys, Self Keys, Shared Keys) + +**Note:** This module is NOT published. diff --git a/getting_started_guide.md b/getting_started_guide.md deleted file mode 100644 index 415f5b78..00000000 --- a/getting_started_guide.md +++ /dev/null @@ -1,52 +0,0 @@ -# Getting Started Guide - -## Requirements - -* [Java 8 or higher](https://www.java.com/en/download/) -* [maven](https://maven.apache.org/download.cgi) - -## Setting Up your environment - -1. [Install java](https://www.java.com/en/download/help/download_options.html) -2. [Install maven](https://maven.apache.org/install.html) -3. Add maven to you path variable -4. Open your terminal -5. cd to `at_java/at_client` -6. run `mvn install`, after instillation you should see `[INFO] BUILD SUCCESS` - -## Registration - -To register an atsign run the following commands in your terminal after -setting up your environment - -* Set proper configurations in at_client/src/main/resources/config.yaml -* run `mvn install` in directory at_java/at_client -* run `java -cp "target/client-1.0-SNAPSHOT.jar:target/lib/*" -org.atsign.client.cli.Register `(Linux) -or -* run `java -cp "target/client-1.0-SNAPSHOT.jar;target/lib/*" -rg.atsign.client.cli.Register `(Windows) - -```text -Getting free atsign -Got atsign: @anxiouswangga5 -Sending one-time-password to : -Got response: Sent Successfully -Enter OTP received on: -``` - -* Enter the OTP received on email provided in the previous step. NB: OTP is -case sensitive: - -```text -xxxx -Validating one-time-password -Got response: Verified -``` - -Congratulations you have sucessfully registered. - -* The .atKeys file for this new atsign is stored in at_client/keys. -**Note:** The default properties in config.yaml point to the testing -environment. Information has been provided in the same file on how to use -the production environment. diff --git a/pom.xml b/pom.xml index ad52a81a..0236459e 100644 --- a/pom.xml +++ b/pom.xml @@ -38,18 +38,18 @@ - 8 - 8 + 11 + 11 UTF-8 config/java-format.xml config/checkstyle.xml - 2.15.4 + 2.16.1 1.78.1 4.7.6 - 2.0.9 + 2.0.13 1.18.30 5.10.2 @@ -333,6 +333,12 @@ + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + +