├── .DS_Store ├── .gitignore ├── README.md ├── configuration ├── aries-args-advanced.yaml ├── aries-args-basic.yaml └── aries-args-multitenant.yaml ├── dockerfiles ├── agents │ ├── Dockerfile │ ├── Dockerfile.attachmentprotocol │ ├── Dockerfile.createconnection │ ├── Dockerfile.ngrok │ └── Dockerfile.ngrok-new └── controllers │ ├── Dockerfile.attachmentcontroller │ └── Dockerfile.basiccontroller ├── helpers └── create_connection │ └── create_connection.py ├── images └── endgame.png ├── libs ├── __init__.py ├── acapy-protocol-example │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── acapy_protocol_example │ │ ├── __init__.py │ │ └── protocolexample │ │ │ ├── __init__.py │ │ │ ├── definition.py │ │ │ └── v1_0 │ │ │ ├── __init__.py │ │ │ ├── handlers │ │ │ ├── __init__.py │ │ │ ├── protocolexample_handler.py │ │ │ └── protocolexample_response_handler.py │ │ │ ├── message_types.py │ │ │ ├── messages │ │ │ ├── __init__.py │ │ │ ├── protocolexample.py │ │ │ └── protocolexample_response.py │ │ │ ├── routes.py │ │ │ └── setup.py │ ├── docker-compose.yml │ ├── entrypoint.sh │ ├── manage │ ├── requirements.txt │ └── setup.py ├── aries-basic-controller │ ├── .gitignore │ ├── Dockerfile.setup │ ├── LICENSE │ ├── README.md │ ├── __init__.py │ ├── aries_basic_controller │ │ ├── __init__.py │ │ ├── aries_controller.py │ │ ├── aries_controller_base.py │ │ ├── aries_tenant_controller.py │ │ ├── aries_webhook_server.py │ │ ├── controllers │ │ │ ├── __init__.py │ │ │ ├── action_menu.py │ │ │ ├── base.py │ │ │ ├── connections.py │ │ │ ├── credential.py │ │ │ ├── definitions.py │ │ │ ├── issuer.py │ │ │ ├── ledger.py │ │ │ ├── mediation.py │ │ │ ├── messaging.py │ │ │ ├── multitenant.py │ │ │ ├── oob.py │ │ │ ├── proof.py │ │ │ ├── revocation.py │ │ │ ├── schema.py │ │ │ ├── server.py │ │ │ └── wallet.py │ │ ├── helpers │ │ │ ├── __init__.py │ │ │ └── utils.py │ │ └── models │ │ │ ├── __init__.py │ │ │ ├── connection.py │ │ │ └── errors.py │ ├── build.sh │ ├── requirements.txt │ └── setup.py ├── attachment-controller │ ├── README.md │ ├── attachment_controller │ │ ├── __init__.py │ │ ├── attachment_controller.py │ │ └── protocol_controller.py │ ├── requirements.txt │ └── setup.py └── attachment-protocol │ ├── README.md │ ├── attach_protocol │ ├── __init__.py │ └── attachment_protocol │ │ ├── __init__.py │ │ ├── definition.py │ │ └── v1_0 │ │ ├── handlers │ │ ├── __init__.py │ │ └── attachment_handler.py │ │ ├── message_types.py │ │ ├── messages │ │ ├── __init__.py │ │ └── attachment.py │ │ ├── routes.py │ │ └── setup.py │ ├── docker-compose.yml │ ├── manage │ ├── requirements.txt │ └── setup.py ├── projects ├── aries-fl │ ├── .DS_Store │ ├── Dockerfile.hospitalcontroller │ ├── Dockerfile.ppmlcontroller │ ├── README.md │ ├── docker-compose.yml │ ├── figures │ │ ├── Trust_to_Hospital.png │ │ ├── VanillaFL.png │ │ ├── coordinator_and_hospital.png │ │ ├── federated_learning.png │ │ ├── initialidea.png │ │ ├── regulator_to_coordinator.png │ │ └── trusted_connection.png │ ├── hospital │ │ ├── __init__.py │ │ └── hospital.py │ ├── manage │ ├── notebooks │ │ ├── .DS_Store │ │ ├── Test │ │ │ ├── .DS_Store │ │ │ ├── Datasets │ │ │ │ ├── coordinator_validation_data.csv │ │ │ │ ├── hospital1.csv │ │ │ │ ├── hospital2.csv │ │ │ │ ├── hospital3.csv │ │ │ │ ├── survey.csv │ │ │ │ └── validation_set.csv │ │ │ ├── Test DP training.ipynb │ │ │ ├── Test Validation.ipynb │ │ │ ├── coordinator_validation_data.csv │ │ │ └── hospital1.csv │ │ ├── hospital1 │ │ │ ├── Hospital1.ipynb │ │ │ ├── Test DP training.ipynb │ │ │ └── data │ │ │ │ ├── hospital1.csv │ │ │ │ ├── hospital2.csv │ │ │ │ └── hospital3.csv │ │ ├── hospital2 │ │ │ ├── Hospital2.ipynb │ │ │ └── data │ │ │ │ └── hospital2.csv │ │ ├── hospital3 │ │ │ ├── Hospital3.ipynb │ │ │ └── data │ │ │ │ └── hospital3.csv │ │ ├── nhs_trust │ │ │ ├── Part 1 - Aries Federated Learning.ipynb │ │ │ ├── Part 2 - Initialise the NHS Trust Agent.ipynb │ │ │ └── Part 3 - Issue Hospital Credentials.ipynb │ │ ├── regulator │ │ │ └── Health Research Regulator.ipynb │ │ └── researcher │ │ │ ├── Researcher.ipynb │ │ │ ├── Test Validation.ipynb │ │ │ ├── data │ │ │ └── coordinator_validation_data.csv │ │ │ └── researcher.py │ ├── requirements.hospital.txt │ └── requirements.txt ├── doctors-in-training │ ├── README.md │ ├── docker-compose.yml │ ├── manage │ ├── notebooks │ │ ├── gmc │ │ │ ├── Part 3.1 - Initialising the GMC Agent.ipynb │ │ │ ├── Part 3.2 - Issue GMC Credential .ipynb │ │ │ └── issuer_agent_invite_QRcode.png │ │ ├── hee │ │ │ ├── Part 4.1 - Initialising the HEE Agent.ipynb │ │ │ ├── Part 4.2 - Interact with Doctor in Training.ipynb │ │ │ ├── issuer_agent_invite_QRcode.png │ │ │ └── verifier_agent_invite_QRcode.png │ │ ├── id-verifier │ │ │ ├── 1. DiT Sequence Diagram B.pdf │ │ │ ├── Part 1.1 - Getting Started.ipynb │ │ │ ├── Part 1.2 - Initialising the ID Verification Agent.ipynb │ │ │ ├── Part 1.3 - Issue Passport Details Credential.ipynb │ │ │ └── id-verifier-agent_invite_QRcode.png │ │ ├── lead-employer │ │ │ ├── Part 5.1 - Initialising the Lead Employer Agent.ipynb │ │ │ ├── Part 5.2 - Onboard a Doctor in Training.ipynb │ │ │ └── issuer_agent_invite_QRcode.png │ │ └── medical-school │ │ │ ├── Part 2.1 - Initialising the Medical School Agent.ipynb │ │ │ ├── Part 2.2 - Issue Medical Qualification.ipynb │ │ │ ├── issuer_agent_invite_QRcode.png │ │ │ └── medical-school-agent_invite_QRcode.png │ └── sequence.jpg ├── opus │ ├── README.md │ ├── demo │ │ ├── alice │ │ │ ├── Opus User Method 2.ipynb │ │ │ └── images │ │ │ │ └── opus_user.png │ │ ├── opus │ │ │ ├── Opus Method 1.ipynb │ │ │ ├── Opus Method 2.ipynb │ │ │ └── images │ │ │ │ ├── credential.png │ │ │ │ ├── opus.png │ │ │ │ ├── opus_1A.png │ │ │ │ ├── opus_1B.png │ │ │ │ ├── opus_2B.png │ │ │ │ └── scheme.png │ │ └── setup │ │ │ └── create_connection.py │ ├── docker-compose.yml │ ├── images │ │ ├── instructions1.png │ │ ├── opus_1A.png │ │ ├── opus_1B.png │ │ └── opus_2B.PNG │ └── manage ├── pryvote │ ├── README.md │ ├── demo │ │ ├── 1_secure_multi_party_voting.ipynb │ │ └── 2_shamir_secret_sharing_voting.ipynb │ └── images │ │ ├── smpv-1.png │ │ └── smpv-2.png └── single-agent │ ├── README.md │ ├── docker-compose.yml │ ├── manage │ └── notebooks │ ├── iiw.png │ ├── issuer_agent_invite_QRcode.png │ └── playground.ipynb ├── scripts ├── clean_notebook_output.sh ├── get_URLS.sh ├── jupyter-entrypoint.sh ├── ngrok-wait.sh ├── parse_yml_env_variables.py ├── startup-new.sh ├── startup.sh └── yml_env_variables.py └── tutorials ├── 1. Learning Aries, ACA-Py and the Basic Controller ├── README.md ├── docker-compose.yml ├── manage └── notebooks │ ├── alice │ ├── 1 Basic Concepts and 1st Connection │ │ ├── Part 1 - Self-Sovereign Identity.ipynb │ │ ├── Part 2 - Aries Basic Controller.ipynb │ │ └── Part 3 - Establishing a Connection.ipynb │ ├── 2 Credentials │ │ ├── Part 1 - Credential Schema and Definitions.ipynb │ │ ├── Part 2 - Issue Credential.ipynb │ │ └── Part 3 - Present Proof.ipynb │ ├── 3 Messages │ │ └── Part 1 - Basic Message.ipynb │ ├── 4 Advanced Concepts │ │ ├── Part 1 - Action Menu.ipynb │ │ ├── Part 2 - Revocation.ipynb │ │ └── Part 3 - Out of Band Protocol.ipynb │ └── APIs │ │ ├── connections_api.ipynb │ │ ├── ledger-api.ipynb │ │ ├── server-api.ipynb │ │ └── wallet_api.ipynb │ └── bob │ ├── 1 Basic Concepts and 1st Connection │ └── Part 3 - Establishing a Connection.ipynb │ ├── 2 Credentials │ ├── Part 2 - Issue Credential.ipynb │ └── Part 3 - Present Proof.ipynb │ ├── 3 Messages │ └── Part 1 - Basic Message.ipynb │ ├── 4 Advanced Concepts │ ├── Part 1 - Action Menu.ipynb │ ├── Part 2 - Revocation.ipynb │ └── Part 3 - Out of Band Protocol.ipynb │ └── APIs │ └── credential-api.ipynb ├── 2. Public Ledgers and Mobile Wallets ├── README.md ├── docker-compose.yml ├── manage └── notebooks │ ├── issuer │ ├── Part 1 - Getting Started.ipynb │ ├── Part 2 - Writing a Public DID to the Sovrin StagingNet.ipynb │ ├── Part 3 - Issue Credential.ipynb │ └── issuer_agent_invite_QRcode.png │ └── verifier │ ├── Part 4 - Verify a Presentation.ipynb │ └── verifier_agent_invite_QRcode.png ├── 3. Attachments ├── README.md ├── docker-compose.yml ├── manage └── notebooks │ ├── alice │ ├── attachment.ipynb │ ├── openmined.jpg │ └── test_file.txt │ └── bob │ └── attachment.ipynb ├── 4. Multitenancy ├── README.md ├── docker-compose.yml ├── manage ├── notebooks │ ├── external │ │ └── Configure External Agent.ipynb │ ├── mediator │ │ └── Configure Mediator.ipynb │ └── multitenant │ │ ├── Alice │ │ ├── Part 2 - Mediation of communication - Alice.ipynb │ │ └── Part 3 - Communicating with an external agent.ipynb │ │ └── Part 1 - Subwallet Administration.ipynb └── resources │ └── init.sql └── 5. OM FoPC Course - Public Key Infrastructures ├── README.md ├── actors ├── dataowner │ ├── example.env │ └── notebooks │ │ ├── 6. Onboarding as a Data Owner.ipynb │ │ └── 7. Establish Authenticated Connection with a Data Scientist.ipynb ├── datascientist │ ├── example.env │ └── notebooks │ │ ├── 5. Onboarding as a Data Scientist.ipynb │ │ └── 7. Connect and Authenticate with a Data Owner.ipynb └── om_authority │ ├── example.env │ └── notebooks │ ├── 1. Start Here.ipynb │ ├── 2. The Aries Basic Controller.ipynb │ ├── 3. Initialising Your Agent as an Issuing Authority.ipynb │ ├── 4. Get Your OM PKI Credential.ipynb │ ├── 5. Onboarding a Data Scientist.ipynb │ ├── 6. Onboarding a Data Owner.ipynb │ └── issuer_agent_invite_QRcode.png ├── docker-compose.yml ├── manage ├── manage_agents_only └── resources └── init.sql /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .venv 3 | .code 4 | dist 5 | *.egg-info 6 | build 7 | .env 8 | .ipynb_checkpoints/ 9 | __pycache__/ 10 | 11 | **/received_files/* 12 | libs/om-aries-controller/demo/bob/image_received.png 13 | 14 | .vscode/ 15 | # exception to the rule 16 | !libs/om-aries-controller/demo/bob/received_files/.gitkeep 17 | 18 | **/*.pt 19 | -------------------------------------------------------------------------------- /configuration/aries-args-advanced.yaml: -------------------------------------------------------------------------------- 1 | # see: https://pypi.org/project/ConfigArgParse/ for file format overview 2 | # before running aca-py, run the following (the commands are embedded below, next to the related parameters): 3 | # - run a local postgres database 4 | # - run a local instance of von-network 5 | # - register your did (seed) on the network 6 | # run aca-py as: 7 | # ACAPY_WALLET_SEED=my_seed_000000000000000000000000 ACAPY_WALLET_KEY=key ./bin/aca-py start --arg-file ./demo/local-indy-args.yaml 8 | 9 | log-level: info 10 | genesis-url: !ENV ${GENESIS_URL} 11 | # Admin insecure mode and api key will come from the following two variables 12 | # which is parsed and used automatically by ACA-Py 13 | # ACAPY_ADMIN_API_KEY 14 | # ACAPY_ADMIN_INSECURE_MODE 15 | admin: [0.0.0.0, !ENV '${ADMIN_PORT}'] 16 | label: !ENV ${AGENT_NAME} 17 | # the following is the callback url for your controller 18 | #webhook-url: !ENV ${WEBHOOK_URL} 19 | # assumes you are running a local von-network, like: 20 | # cd von-network 21 | # ./manage start 22 | inbound-transport: 23 | - [http, 0.0.0.0, !ENV '${HTTP_PORT}'] 24 | # - [ws, 0.0.0.0, !ENV '${HTTP_PORT}'] 25 | outbound-transport: http 26 | # the following is the public endpoint advertised by the agent 27 | endpoint: !ENV ${AGENT_ENDPOINT} 28 | auto-ping-connection: true 29 | # register your did using (this example is for von-network): 30 | # curl -d '{"seed":"my_seed_000000000000000000000000", "role":"TRUST_ANCHOR", "alias":"My Agent"}' -X POST http://localhost:9000/register 31 | # note that the env var name is configured in argparse.py 32 | # seed = comes from ACAPY_WALLET_SEED 33 | wallet-type: !ENV ${WALLET_TYPE} 34 | wallet-name: !ENV ${WALLET_NAME} 35 | wallet-key: !ENV ${WALLET_KEY} 36 | #seed: !ENV ${WALLET_SEED} 37 | 38 | ## run a local postgres (docker) like: 39 | ## docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d -p 5432:5432 postgres:10 40 | #wallet-storage-type: postgres_storage 41 | ## could be sent using env var ACAPY_WALLET_STORAGE_CONFIG 42 | #wallet-storage-config: '{"url":"localhost:5432","max_connections":5}' 43 | ## could be sent using env var ACAPY_WALLET_STORAGE_CREDS 44 | #wallet-storage-creds: '{"account":"postgres","password":"mysecretpassword","admin_account":"postgres","admin_password":"mysecretpassword"}' 45 | 46 | 47 | auto-accept-requests: true 48 | auto-provision: true 49 | auto-respond-credential-proposal: true 50 | auto-respond-credential-offer: true 51 | auto-respond-credential-request: true 52 | auto-store-credential: true 53 | auto-respond-presentation-proposal: true 54 | auto-respond-presentation-request: true 55 | preserve-exchange-records: true 56 | debug-connections: true 57 | debug-credentials: true 58 | debug-presentations: true 59 | enable-undelivered-queue: true 60 | -------------------------------------------------------------------------------- /configuration/aries-args-basic.yaml: -------------------------------------------------------------------------------- 1 | # see: https://pypi.org/project/ConfigArgParse/ for file format overview 2 | # before running aca-py, run the following (the commands are embedded below, next to the related parameters): 3 | # - run a local postgres database 4 | # - run a local instance of von-network 5 | # - register your did (seed) on the network 6 | # run aca-py as: 7 | # ACAPY_WALLET_SEED=my_seed_000000000000000000000000 ACAPY_WALLET_KEY=key ./bin/aca-py start --arg-file ./demo/local-indy-args.yaml 8 | 9 | log-level: info 10 | genesis-url: !ENV ${GENESIS_URL} 11 | # Admin insecure mode and api key will come from the following two variables 12 | # which is parsed and used automatically by ACA-Py 13 | # ACAPY_ADMIN_API_KEY 14 | # ACAPY_ADMIN_INSECURE_MODE 15 | admin: [0.0.0.0, !ENV '${ADMIN_PORT}'] 16 | label: !ENV ${AGENT_NAME} 17 | # the following is the callback url for your controller 18 | #webhook-url: !ENV ${WEBHOOK_URL} 19 | # assumes you are running a local von-network, like: 20 | # cd von-network 21 | # ./manage start 22 | inbound-transport: 23 | - [http, 0.0.0.0, !ENV '${HTTP_PORT}'] 24 | # - [ws, 0.0.0.0, !ENV '${HTTP_PORT}'] 25 | outbound-transport: http 26 | # the following is the public endpoint advertised by the agent 27 | endpoint: !ENV ${AGENT_ENDPOINT} 28 | # register your did using (this example is for von-network): 29 | # curl -d '{"seed":"my_seed_000000000000000000000000", "role":"TRUST_ANCHOR", "alias":"My Agent"}' -X POST http://localhost:9000/register 30 | # note that the env var name is configured in argparse.py 31 | # seed = comes from ACAPY_WALLET_SEED 32 | wallet-type: !ENV ${WALLET_TYPE} 33 | wallet-name: !ENV ${WALLET_NAME} 34 | wallet-key: !ENV ${WALLET_KEY} 35 | seed: !ENV ${WALLET_SEED} 36 | auto-provision: true 37 | 38 | 39 | ## run a local postgres (docker) like: 40 | ## docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d -p 5432:5432 postgres:10 41 | #wallet-storage-type: postgres_storage 42 | ## could be sent using env var ACAPY_WALLET_STORAGE_CONFIG 43 | #wallet-storage-config: '{"url":"localhost:5432","max_connections":5}' 44 | ## could be sent using env var ACAPY_WALLET_STORAGE_CREDS 45 | #wallet-storage-creds: '{"account":"postgres","password":"mysecretpassword","admin_account":"postgres","admin_password":"mysecretpassword"}' -------------------------------------------------------------------------------- /configuration/aries-args-multitenant.yaml: -------------------------------------------------------------------------------- 1 | # see: https://pypi.org/project/ConfigArgParse/ for file format overview 2 | # before running aca-py, run the following (the commands are embedded below, next to the related parameters): 3 | # - run a local postgres database 4 | # - run a local instance of von-network 5 | # - register your did (seed) on the network 6 | # run aca-py as: 7 | # ACAPY_WALLET_SEED=my_seed_000000000000000000000000 ACAPY_WALLET_KEY=key ./bin/aca-py start --arg-file ./demo/local-indy-args.yaml 8 | 9 | log-level: info 10 | genesis-url: !ENV ${GENESIS_URL} 11 | # Admin insecure mode and api key will come from the following two variables 12 | # which is parsed and used automatically by ACA-Py 13 | # ACAPY_ADMIN_API_KEY 14 | # ACAPY_ADMIN_INSECURE_MODE 15 | admin: [0.0.0.0, !ENV '${ADMIN_PORT}'] 16 | label: !ENV ${AGENT_NAME} 17 | # the following is the callback url for your controller 18 | #webhook-url: !ENV ${WEBHOOK_URL} 19 | # assumes you are running a local von-network, like: 20 | # cd von-network 21 | # ./manage start 22 | inbound-transport: 23 | - [http, 0.0.0.0, !ENV '${HTTP_PORT}'] 24 | # - [ws, 0.0.0.0, !ENV '${HTTP_PORT}'] 25 | outbound-transport: http 26 | # the following is the public endpoint advertised by the agent 27 | endpoint: !ENV ${AGENT_ENDPOINT} 28 | auto-ping-connection: true 29 | # register your did using (this example is for von-network): 30 | # curl -d '{"seed":"my_seed_000000000000000000000000", "role":"TRUST_ANCHOR", "alias":"My Agent"}' -X POST http://localhost:9000/register 31 | # note that the env var name is configured in argparse.py 32 | # seed = comes from ACAPY_WALLET_SEED 33 | wallet-type: !ENV ${WALLET_TYPE} 34 | wallet-name: !ENV ${WALLET_NAME} 35 | wallet-key: !ENV ${WALLET_KEY} 36 | seed: !ENV ${WALLET_SEED} 37 | 38 | ## run a local postgres (docker) like: 39 | ## docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d -p 5432:5432 postgres:10 40 | #wallet-storage-type: postgres_storage 41 | ## could be sent using env var ACAPY_WALLET_STORAGE_CONFIG 42 | #wallet-storage-config: '{"url":"localhost:5432","max_connections":5}' 43 | ## could be sent using env var ACAPY_WALLET_STORAGE_CREDS 44 | #wallet-storage-creds: '{"account":"postgres","password":"mysecretpassword","admin_account":"postgres","admin_password":"mysecretpassword"}' 45 | 46 | 47 | auto-accept-requests: true 48 | auto-provision: true 49 | auto-respond-credential-proposal: false 50 | auto-respond-credential-offer: false 51 | auto-respond-credential-request: false 52 | auto-store-credential: false 53 | auto-respond-presentation-proposal: true 54 | auto-respond-presentation-request: true 55 | preserve-exchange-records: true 56 | debug-connections: true 57 | debug-credentials: true 58 | debug-presentations: true 59 | enable-undelivered-queue: true 60 | open-mediation: true 61 | -------------------------------------------------------------------------------- /dockerfiles/agents/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM bcgovimages/aries-cloudagent:py36-1.15-1_0.6.0 2 | 3 | ADD configuration ./configuration 4 | ADD scripts ./scripts -------------------------------------------------------------------------------- /dockerfiles/agents/Dockerfile.attachmentprotocol: -------------------------------------------------------------------------------- 1 | FROM bcgovimages/aries-cloudagent:py36-1.15-1_0.6.0 2 | 3 | ADD configuration ./configuration 4 | ADD scripts ./scripts 5 | 6 | USER root 7 | 8 | ADD libs/attachment-protocol . 9 | 10 | 11 | RUN pip3 install --no-cache-dir -e . 12 | # 13 | USER $user 14 | -------------------------------------------------------------------------------- /dockerfiles/agents/Dockerfile.createconnection: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | ADD libs/aries-basic-controller . 4 | 5 | 6 | RUN pip3 install --no-cache-dir -e . 7 | 8 | ADD helpers/create_connection/create_connection.py . 9 | -------------------------------------------------------------------------------- /dockerfiles/agents/Dockerfile.ngrok: -------------------------------------------------------------------------------- 1 | FROM bcgovimages/aries-cloudagent:py36-1.15-1_0.6.0 2 | 3 | ADD configuration ./configuration 4 | ADD scripts ./scripts 5 | 6 | USER root 7 | 8 | ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 ./jq 9 | RUN chmod +x ./jq 10 | 11 | RUN chmod +x ./scripts/startup.sh 12 | RUN chmod +x ./scripts/ngrok-wait.sh 13 | 14 | USER $user 15 | 16 | ENTRYPOINT ["/usr/bin/env"] 17 | CMD ./scripts/ngrok-wait.sh ./scripts/startup.sh -------------------------------------------------------------------------------- /dockerfiles/agents/Dockerfile.ngrok-new: -------------------------------------------------------------------------------- 1 | FROM bcgovimages/aries-cloudagent:py36-1.15-1_0.6.0 2 | 3 | ADD configuration ./configuration 4 | ADD scripts ./scripts 5 | 6 | USER root 7 | 8 | ADD https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 ./jq 9 | RUN chmod +x ./jq 10 | 11 | RUN chmod +x ./scripts/startup-new.sh 12 | RUN chmod +x ./scripts/ngrok-wait.sh 13 | 14 | USER $user 15 | 16 | ENTRYPOINT ["/usr/bin/env"] 17 | CMD ./scripts/ngrok-wait.sh ./scripts/startup-new.sh -------------------------------------------------------------------------------- /dockerfiles/controllers/Dockerfile.attachmentcontroller: -------------------------------------------------------------------------------- 1 | FROM continuumio/miniconda3 2 | 3 | ENV WORKSPACE /workspace 4 | 5 | ARG jupyter_port 6 | ENV env_jupyter_port=jupyter_port 7 | # Setup workspace environment 8 | RUN apt-get update && apt-get install -y gcc 9 | # RUN conda install -c conda-forge jupyterlab==3.0.11 10 | RUN conda install -c conda-forge jupyterlab==3.0.11 11 | 12 | ADD libs/attachment-controller attachment-controller 13 | 14 | 15 | RUN pip install --no-cache-dir -e attachment-controller 16 | 17 | 18 | WORKDIR $WORKSPACE 19 | 20 | # Make the image start the jupyer notebook 21 | COPY scripts/jupyter-entrypoint.sh /entrypoint.sh 22 | RUN chmod +x /entrypoint.sh 23 | 24 | # ENTRYPOINT ["/entrypoint.sh", "$env_jupyter_port"] 25 | ENTRYPOINT ["/entrypoint.sh", "8888"] -------------------------------------------------------------------------------- /dockerfiles/controllers/Dockerfile.basiccontroller: -------------------------------------------------------------------------------- 1 | FROM continuumio/miniconda3 2 | 3 | ENV WORKSPACE /workspace 4 | 5 | ARG jupyter_port 6 | ENV env_jupyter_port=jupyter_port 7 | 8 | # Setup workspace environment 9 | RUN apt-get update && apt-get install -y gcc 10 | # RUN conda install -c conda-forge jupyterlab==3.0.11 11 | RUN conda install -c conda-forge jupyterlab==3.0.11 12 | ADD libs/aries-basic-controller . 13 | 14 | # 15 | # 16 | RUN pip install --no-cache-dir -e . 17 | #RUN pip install -r requirements.txt 18 | 19 | #RUN pip install aiohttp 20 | #RUN export PYTHONPATH="$PYTHONPATH:/aries_basic_controller" 21 | #ENV PYTHONPATH "${PYTHONPATH}:/aries_basic_controller" 22 | #RUN export JUPYTER_PATH="$JUPYTER_PATH:/aries_basic_controller" 23 | # Create jupyter notebook workspace 24 | #RUN mkdir $WORKSPACE 25 | WORKDIR $WORKSPACE 26 | 27 | # Make the image start the jupyer notebook 28 | COPY scripts/jupyter-entrypoint.sh /entrypoint.sh 29 | RUN chmod +x /entrypoint.sh 30 | 31 | 32 | 33 | 34 | 35 | # ENTRYPOINT ["/entrypoint.sh", "$env_jupyter_port"] 36 | ENTRYPOINT ["/entrypoint.sh", "8888"] -------------------------------------------------------------------------------- /helpers/create_connection/create_connection.py: -------------------------------------------------------------------------------- 1 | import os 2 | import asyncio 3 | import time 4 | 5 | from aries_basic_controller.aries_controller import AriesAgentController 6 | 7 | from dotenv import load_dotenv 8 | load_dotenv() 9 | 10 | ALICE_ADMIN_URL = os.getenv('ALICE_ADMIN_URL') 11 | ALICE_WEBHOOK_PORT = os.getenv('ALICE_WEBHOOK_PORT') 12 | ALICE_WEBHOOK_HOST = os.getenv('ALICE_WEBHOOK_HOST') 13 | ALICE_WEBHOOK_BASE = os.getenv('ALICE_WEBHOOK_BASE') 14 | ALICE_API_KEY = os.getenv('ALICE_API_KEY') 15 | 16 | BOB_ADMIN_URL = os.getenv('BOB_ADMIN_URL') 17 | BOB_WEBHOOK_PORT = os.getenv('BOB_WEBHOOK_PORT') 18 | BOB_WEBHOOK_HOST = os.getenv('BOB_WEBHOOK_HOST') 19 | BOB_WEBHOOK_BASE = os.getenv('BOB_WEBHOOK_BASE') 20 | 21 | 22 | async def start_agent(): 23 | 24 | time.sleep(10) 25 | 26 | # Inviter 27 | bob_agent_controller = AriesAgentController(admin_url=BOB_ADMIN_URL) 28 | 29 | # Invitee 30 | alice_agent_controller = AriesAgentController( 31 | admin_url=ALICE_ADMIN_URL, 32 | api_key=ALICE_API_KEY) 33 | 34 | # alice_agent_controller.init_webhook_server( 35 | # webhook_host=ALICE_WEBHOOK_HOST, 36 | # webhook_port=ALICE_WEBHOOK_PORT, 37 | # webhook_base=ALICE_WEBHOOK_BASE) 38 | 39 | # await alice_agent_controller.listen_webhooks() 40 | # 41 | # await bob_agent_controller.listen_webhooks() 42 | # 43 | # 44 | # bob_agent_controller.register_listeners([], defaults=True) 45 | # alice_agent_controller.register_listeners([], defaults=True) 46 | 47 | invite = await bob_agent_controller.connections.create_invitation() 48 | print("Invite from BOB", invite) 49 | 50 | bob_connection_id = invite["connection_id"] 51 | print("Bob's connection ID for Alice", bob_connection_id) 52 | 53 | response = await alice_agent_controller.connections.accept_connection(invite["invitation"]) 54 | 55 | 56 | print("Alice's Connection ID for Bob", response["connection_id"]) 57 | alice_id = response["connection_id"] 58 | print("Invite Accepted") 59 | print("Alice's state for Bob's connection :", response["state"]) 60 | 61 | 62 | time.sleep(10) 63 | 64 | # connection = await bob_agent_controller.connections.get_connection(bob_connection_id) 65 | # while connection["state"] != "request": 66 | # time.sleep(1) 67 | # connection = await bob_agent_controller.connections.get_connection(bob_connection_id) 68 | 69 | connection = await bob_agent_controller.connections.get_connection(bob_connection_id) 70 | print("Bob's Connection State for Alice :", connection["state"]) 71 | 72 | all_conns = await bob_agent_controller.connections.get_connections() 73 | print("All Conns : ", all_conns) 74 | 75 | 76 | connection = await bob_agent_controller.connections.accept_request(bob_connection_id) 77 | print("Request Accepted") 78 | print(connection) 79 | 80 | print("BOB AGENT CONNECTION") 81 | print(connection) 82 | 83 | while connection["state"] != "active": 84 | trust_ping = await bob_agent_controller.messaging.trust_ping(bob_connection_id, "hello") 85 | print("TUST PING TO ACTIVATE CONNECTION - BOB -> RESEARCH") 86 | print(trust_ping) 87 | time.sleep(5) 88 | connection = await bob_agent_controller.connections.get_connection(bob_connection_id) 89 | 90 | trust_ping = await alice_agent_controller.messaging.trust_ping(alice_id,"hello") 91 | print("TUST PING TO ACTIVATE CONNECTION - RESEARCH -> BOB") 92 | print(trust_ping) 93 | 94 | print("ALICE ID {} BOB ID {}".format(alice_id, bob_connection_id)) 95 | 96 | connection = await bob_agent_controller.connections.get_connection(bob_connection_id) 97 | print("BOB AGENT CONNECTION") 98 | print(connection) 99 | 100 | connection = await alice_agent_controller.connections.get_connection(alice_id) 101 | print("RESEARCH AGENT CONNECTION") 102 | print(connection) 103 | 104 | print("SUCCESS") 105 | time.sleep(2) 106 | await bob_agent_controller.terminate() 107 | await alice_agent_controller.terminate() 108 | 109 | 110 | if __name__ == "__main__": 111 | # time.sleep(60) 112 | try: 113 | asyncio.get_event_loop().run_until_complete(start_agent()) 114 | except KeyboardInterrupt: 115 | os._exit(1) 116 | -------------------------------------------------------------------------------- /images/endgame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/images/endgame.png -------------------------------------------------------------------------------- /libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/__init__.py -------------------------------------------------------------------------------- /libs/acapy-protocol-example/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | env 3 | **.egg-info 4 | .idea/ 5 | 6 | # Set the default behavior, in case people don't have core.autocrlf set. 7 | * text=auto 8 | 9 | # Declare files that will always have LF line endings on checkout. 10 | *.sh text eol=lf -------------------------------------------------------------------------------- /libs/acapy-protocol-example/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM bcgovimages/von-image:py36-1.14-1 2 | 3 | USER root 4 | 5 | ADD . . 6 | 7 | RUN pip3 install --no-cache-dir -e . 8 | # 9 | USER $user 10 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/README.md: -------------------------------------------------------------------------------- 1 | # Aries ACA-Py Protocol Example 2 | 3 | This repository contains a basic example of a Hyperledger Aries protocol implemented in python and included into the [ACA-Py cloud-agent](https://github.com/hyperledger/aries-cloudagent-python) as a plugin. 4 | 5 | Hyperledger Aries is a project enabling trusted online peer-to-peer interactions through a set of protocols outlined in [aries-rfcs](https://github.com/hyperledger/aries-rfcs). 6 | 7 | If you are new to Hyperledger Aries I recommend working through these two Edx courses: 8 | * [Introduction to Hyperledger Sovereign Identity Blockchain Solutions: Indy, Aries & Ursa](https://www.edx.org/course/identity-in-hyperledger-aries-indy-and-ursa) 9 | * [Becoming a Hyperledger Aries Developer](https://www.edx.org/course/becoming-a-hyperledger-aries-developer) 10 | 11 | This is a basic example of a protocol implemented for an aca-py agent and included as a plugin. It can be thought of as an extension to [Chapter 5](https://courses.edx.org/courses/course-v1:LinuxFoundationX+LFS173x+1T2020/course/#block-v1:LinuxFoundationX+LFS173x+1T2020+type@chapter+block@002f6693698443ceb77443a8d50cb974) of the Becoming a Hyperledger Aries Developer edx course. 12 | 13 | I think it is worth reiterating some of the content from Chapter 5 - it took me a while to internalize this and appreciate what it means from a development perspective. Hyperledger Aries is a set of specifications for protocols expressed as [aries-rfcs](https://github.com/hyperledger/aries-rfcs). These specifications can be implemented in any language and enable aries agents to be spun up, much like a server (aca-py start instead of express().listen()). 14 | 15 | The primary use case for agents is secure, private peer-to-peer communication with other agents using a protocol called DIDComm. DIDComm defines how messages should be secured and sent to other agents, but does not describe the content of these messages. The majority of aries protocols are simply defining a set of messages the get communicated back and forth between agents. Hyperledger aries defines a set of protocols as [aries-rfcs](https://github.com/hyperledger/aries-rfcs/tree/master/features), aswell as an [interoperability profile](https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0302-aries-interop-profile) defining the core set of protocols agents must implement if they wish to be interoperable. 16 | 17 | In addition to the protocol's already implemented in Hyperledger aries projects, it is possible to define protocols for specific usecases. As long as you describe in the code of the agent a set of **Messages**, each with their own unique **MessageType**, **MessageSchema** and **Handler**. This is so that when the agent recieves a **Message** of a particular **MessageType**, it understands the expected structure of the message and how to **Handle** it. Handling a **Message** can include sending a webhook to the **Controller** or replying to the sender with a new Message. 18 | 19 | I have uploaded a walkthrough [video that dives into the ACA-Py implementation details of a protocol](https://www.youtube.com/watch?v=HjD-fasHmX8). 20 | 21 | To run through the example clone the repo then run `./manage up`. This starts 2 aca-py agents with the plugin protocolexample included and defined in acapy_protocol_example.protocolexample. 22 | 23 | You can navigate to the swagger API for the two agents at: 24 | * http://localhost:8021/api/doc 25 | * http://localhost:8051/api/doc 26 | 27 | Simply create a connection between the two agents using the swagger interface - follow [this demo](https://github.com/hyperledger/aries-cloudagent-python/blob/master/demo/AriesOpenAPIDemo.md) if you are unsure how. Then invoke the protocolexample protocol using the swagger interface to send a protocolexample message from one agent to the other. You should see this in the logs. 28 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/acapy-protocol-example/acapy_protocol_example/__init__.py -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/acapy-protocol-example/acapy_protocol_example/protocolexample/__init__.py -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/definition.py: -------------------------------------------------------------------------------- 1 | """Version definitions for this protocol.""" 2 | 3 | versions = [ 4 | { 5 | "major_version": 1, 6 | "minimum_minor_version": 0, 7 | "current_minor_version": 0, 8 | "path": "v1_0", 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/__init__.py -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/handlers/__init__.py -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/handlers/protocolexample_handler.py: -------------------------------------------------------------------------------- 1 | from aries_cloudagent.messaging.base_handler import ( 2 | BaseHandler, 3 | BaseResponder, 4 | RequestContext, 5 | ) 6 | 7 | from ..messages.protocolexample import ProtocolExample 8 | from ..messages.protocolexample_response import ProtocolExampleResponse 9 | 10 | class ProtocolExampleHandler(BaseHandler): 11 | 12 | async def handle(self, context: RequestContext, responder: BaseResponder): 13 | 14 | self._logger.info(f"ProtocolExampleHandler called") 15 | assert isinstance(context.message, ProtocolExample) 16 | 17 | self._logger.info( 18 | "Received protocolexample message from: %s with content - %s", context.message_receipt.sender_did, context.message 19 | ) 20 | 21 | if not context.connection_ready: 22 | self._logger.info( 23 | "Connection not active, skipping protocolexample response: %s", 24 | context.message_receipt.sender_did, 25 | ) 26 | return 27 | 28 | 29 | if context.message.response_requested: 30 | self._logger.info("Response requested, sending ...") 31 | reply = ProtocolExampleResponse(example_response="Thank you for your protocol message") 32 | reply.assign_thread_from(context.message) 33 | reply.assign_trace_from(context.message) 34 | await responder.send_reply(reply) 35 | 36 | self._logger.info("Send webhook with topic protocolexample") 37 | await responder.send_webhook( 38 | "protocolexample", 39 | { 40 | "example": context.message.example, 41 | "connection_id": context.message_receipt.connection_id, 42 | "state": "received", 43 | "thread_id": context.message._thread_id, 44 | }, 45 | ) 46 | 47 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/handlers/protocolexample_response_handler.py: -------------------------------------------------------------------------------- 1 | """Ping response handler.""" 2 | 3 | from aries_cloudagent.messaging.base_handler import ( 4 | BaseHandler, 5 | BaseResponder, 6 | RequestContext, 7 | ) 8 | 9 | from ..messages.protocolexample_response import ProtocolExampleResponse 10 | 11 | 12 | class ProtocolExampleResponseHandler(BaseHandler): 13 | """Protocol Example response handler class.""" 14 | 15 | async def handle(self, context: RequestContext, responder: BaseResponder): 16 | """ 17 | Handle protocolexample response message. 18 | 19 | Args: 20 | context: Request context 21 | responder: Responder used to reply 22 | 23 | """ 24 | 25 | self._logger.info("ProtocolExampleResponseHandler called") 26 | assert isinstance(context.message, ProtocolExampleResponse) 27 | 28 | self._logger.info( 29 | "Received protocolexample response from: %s with content - %s", context.message_receipt.sender_did, context.message 30 | ) 31 | 32 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/message_types.py: -------------------------------------------------------------------------------- 1 | 2 | PROTOCOL_URI = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/protocolexample/1.0" 3 | 4 | PROTOCOL_EXAMPLE = f"{PROTOCOL_URI}/protocolexample" 5 | PROTOCOL_EXAMPLE_RESPONSE = f"{PROTOCOL_URI}/protocolexample_response" 6 | 7 | NEW_PROTOCOL_URI = "https://didcomm.org/protocolexample/1.0" 8 | 9 | NEW_PPML = f"{NEW_PROTOCOL_URI}/protocolexample" 10 | NEW_PPML_RESPONSE = f"{NEW_PROTOCOL_URI}/protocolexample_response" 11 | 12 | PROTOCOL_PACKAGE = "acapy_protocol_example.protocolexample.v1_0" 13 | 14 | MESSAGE_TYPES = { 15 | PROTOCOL_EXAMPLE: f"{PROTOCOL_PACKAGE}.messages.protocolexample.ProtocolExample", 16 | PROTOCOL_EXAMPLE_RESPONSE: f"{PROTOCOL_PACKAGE}.messages.protocolexample_response.ProtocolExampleResponse", 17 | NEW_PPML: f"{PROTOCOL_PACKAGE}.messages.protocolexample.ProtocolExample", 18 | NEW_PPML_RESPONSE: f"{PROTOCOL_PACKAGE}.messages.protocolexample_response.ProtocolExampleResponse", 19 | } 20 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/messages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/messages/__init__.py -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/messages/protocolexample.py: -------------------------------------------------------------------------------- 1 | """Represents a Privacy Preserving Machine Learning Message""" 2 | 3 | from ..message_types import PROTOCOL_EXAMPLE, PROTOCOL_PACKAGE 4 | 5 | from marshmallow import fields 6 | 7 | 8 | from aries_cloudagent.messaging.agent_message import AgentMessage, AgentMessageSchema 9 | 10 | HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.protocolexample_handler.ProtocolExampleHandler" 11 | 12 | 13 | class ProtocolExample(AgentMessage): 14 | """Class representing PPML Message""" 15 | 16 | class Meta: 17 | 18 | handler_class = HANDLER_CLASS 19 | message_type = PROTOCOL_EXAMPLE 20 | schema_class = "ProtocolExampleSchema" 21 | 22 | def __init__( 23 | self, *, response_requested: bool = True, example: str = None, **kwargs 24 | ): 25 | super(ProtocolExample, self).__init__(**kwargs) 26 | self.example = example 27 | self.response_requested = response_requested 28 | 29 | 30 | class ProtocolExampleSchema(AgentMessageSchema): 31 | """Schema for Ppml class.""" 32 | 33 | class Meta: 34 | """PpmlSchema metadata.""" 35 | 36 | model_class = ProtocolExample 37 | 38 | response_requested = fields.Bool( 39 | default=True, 40 | required=False, 41 | description="Whether response is requested (default True)", 42 | example=True, 43 | ) 44 | example = fields.Str( 45 | required=False, 46 | description="This is an example of a string field in your message", 47 | example="this is an example", 48 | allow_none=False 49 | ) 50 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/messages/protocolexample_response.py: -------------------------------------------------------------------------------- 1 | 2 | from marshmallow import fields 3 | 4 | from aries_cloudagent.messaging.agent_message import AgentMessage, AgentMessageSchema 5 | 6 | from ..message_types import PROTOCOL_EXAMPLE_RESPONSE, PROTOCOL_PACKAGE 7 | 8 | 9 | HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.protocolexample_response_handler.ProtocolExampleResponseHandler" 10 | 11 | 12 | class ProtocolExampleResponse(AgentMessage): 13 | 14 | class Meta: 15 | 16 | handler_class = HANDLER_CLASS 17 | message_type = PROTOCOL_EXAMPLE_RESPONSE 18 | schema_class = "ProtocolExampleResponseSchema" 19 | 20 | def __init__(self, *, example_repsonse: str = None, **kwargs): 21 | 22 | super(ProtocolExampleResponse, self).__init__(kwargs) 23 | self.example_repsonse = example_repsonse 24 | 25 | 26 | class ProtocolExampleResponseSchema(AgentMessageSchema): 27 | 28 | class Meta: 29 | 30 | model_class = ProtocolExampleResponse 31 | 32 | example_response = fields.Str( 33 | required=False, 34 | description="This is an example of a response string", 35 | example="protocol received response", 36 | allow_none=True 37 | ) 38 | 39 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/routes.py: -------------------------------------------------------------------------------- 1 | """Protocol example admin routes.""" 2 | 3 | from aiohttp import web 4 | from aiohttp_apispec import docs, match_info_schema, request_schema, response_schema 5 | 6 | from marshmallow import fields, Schema 7 | 8 | from aries_cloudagent.connections.models.conn_record import ConnRecord 9 | from aries_cloudagent.messaging.valid import UUIDFour 10 | from aries_cloudagent.storage.error import StorageNotFoundError 11 | from aries_cloudagent.admin.request_context import AdminRequestContext 12 | 13 | from .messages.protocolexample import ProtocolExample 14 | 15 | class ProtocolExampleRequestSchema(Schema): 16 | 17 | example = fields.Str(required=True, description="An example field in a http request") 18 | response_requested = fields.Bool(required=False, description="Set to true if you want for a ProtocolExampleResponse to this message") 19 | 20 | 21 | class ProtocolExampleRequestResponseSchema(Schema): 22 | 23 | thread_id = fields.Str(required=False, description="Thread ID of the ping message") 24 | 25 | 26 | class ConnIdMatchInfoSchema(Schema): 27 | """Path parameters and validators for request taking connection id.""" 28 | 29 | conn_id = fields.Str( 30 | description="Connection identifier", required=True, example=UUIDFour.EXAMPLE 31 | ) 32 | 33 | 34 | @docs(tags=["protocol example routes"], summary="Tell agent to invoke the protocolexample") 35 | @match_info_schema(ConnIdMatchInfoSchema()) 36 | @request_schema(ProtocolExampleRequestSchema()) 37 | @response_schema(ProtocolExampleRequestResponseSchema(), 200) 38 | async def connections_send_ping(request: web.BaseRequest): 39 | """ 40 | Request handler for sending a trust ping to a connection. 41 | 42 | Args: 43 | request: aiohttp request object 44 | 45 | """ 46 | context: AdminRequestContext = request["context"] 47 | connection_id = request.match_info["conn_id"] 48 | outbound_handler = request["outbound_message_router"] 49 | session = await context.session() 50 | 51 | body = await request.json() 52 | example = body.get("example") 53 | response_requested = body.get("response_requested") 54 | 55 | try: 56 | connection = await ConnRecord.retrieve_by_id(session, connection_id) 57 | except StorageNotFoundError: 58 | raise web.HTTPNotFound() 59 | 60 | if not connection.is_ready: 61 | raise web.HTTPBadRequest() 62 | 63 | msg = ProtocolExample(example=example, response_requested=response_requested) 64 | await outbound_handler(msg, connection_id=connection_id) 65 | 66 | return web.json_response({"thread_id": msg._thread_id}) 67 | 68 | 69 | async def register(app: web.Application): 70 | """Register routes.""" 71 | 72 | app.add_routes( 73 | [web.post("/connections/{conn_id}/test-protocolexample", connections_send_ping)] 74 | ) -------------------------------------------------------------------------------- /libs/acapy-protocol-example/acapy_protocol_example/protocolexample/v1_0/setup.py: -------------------------------------------------------------------------------- 1 | from aries_cloudagent.config.injection_context import InjectionContext 2 | from aries_cloudagent.core.protocol_registry import ProtocolRegistry 3 | from .message_types import MESSAGE_TYPES 4 | 5 | 6 | async def setup( 7 | context: InjectionContext, 8 | protocol_registry: ProtocolRegistry = None 9 | ): 10 | """Setup the protocolexample plugin.""" 11 | if not protocol_registry: 12 | protocol_registry = await context.inject(ProtocolRegistry) 13 | protocol_registry.register_message_types( 14 | MESSAGE_TYPES 15 | ) 16 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | alice-agent: 4 | image: agent:alice 5 | build: . 6 | environment: 7 | - ADMIN_PORT:${ALICE_ADMIN_PORT} 8 | - HTTP_PORT:${ALICE_HTTP_PORT} 9 | ports: 10 | - ${ALICE_HTTP_PORT}:${ALICE_HTTP_PORT} 11 | - ${ALICE_ADMIN_PORT}:${ALICE_ADMIN_PORT} 12 | entrypoint: /bin/bash 13 | command: [ 14 | "-c", 15 | " 16 | aca-py start \ 17 | --inbound-transport http '0.0.0.0' ${ALICE_HTTP_PORT} \ 18 | --outbound-transport http \ 19 | --endpoint ${ALICE_AGENT_ENDPOINT} \ 20 | --wallet-type 'indy' \ 21 | --seed '${ALICE_WALLET_SEED}' \ 22 | --admin '0.0.0.0' ${ALICE_ADMIN_PORT} \ 23 | --admin-insecure-mode \ 24 | --log-level info \ 25 | --plugin acapy_protocol_example.protocolexample \ 26 | --label ${ALICE_AGENT_NAME}", 27 | ] 28 | bob-agent: 29 | image: agent:bob 30 | build: . 31 | environment: 32 | - ADMIN_PORT:${BOB_ADMIN_PORT} 33 | - HTTP_PORT:${BOB_HTTP_PORT} 34 | ports: 35 | - ${BOB_HTTP_PORT}:${BOB_HTTP_PORT} 36 | - ${BOB_ADMIN_PORT}:${BOB_ADMIN_PORT} 37 | entrypoint: /bin/bash 38 | command: [ 39 | "-c", 40 | "aca-py start \ 41 | --inbound-transport http '0.0.0.0' ${BOB_HTTP_PORT} \ 42 | --outbound-transport http \ 43 | --endpoint ${BOB_AGENT_ENDPOINT} \ 44 | --wallet-type 'indy' \ 45 | --seed '${BOB_WALLET_SEED}' \ 46 | --admin '0.0.0.0' ${BOB_ADMIN_PORT} \ 47 | --admin-insecure-mode \ 48 | --log-level info \ 49 | --plugin acapy_protocol_example.protocolexample \ 50 | --label ${BOB_AGENT_NAME}", 51 | ] 52 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Use the passed WORKSPACE DIRECTORY 4 | # Otherwise use /workspace 5 | if [ -z "${WORKSPACE_DIR}" ]; then 6 | WORKSPACE="/workspace" 7 | else 8 | WORKSPACE="${WORKSPACE_DIR}" 9 | fi 10 | 11 | cd $WORKSPACE 12 | echo "HELLO THIS IS PORT $1" 13 | jupyter lab --ip=0.0.0.0 --port="$1" --allow-root 14 | -------------------------------------------------------------------------------- /libs/acapy-protocol-example/requirements.txt: -------------------------------------------------------------------------------- 1 | aries-cloudagent[indy]@git+https://github.com/hyperledger/aries-cloudagent-python 2 | flake8 3 | python-dateutil 4 | pyyaml>=5.4 # not directly required, pinned by Snyk to avoid a vulnerability -------------------------------------------------------------------------------- /libs/acapy-protocol-example/setup.py: -------------------------------------------------------------------------------- 1 | """aries-acapy-protocol-example""" 2 | 3 | from setuptools import setup, find_packages 4 | 5 | 6 | def parse_requirements(filename): 7 | """Load requirements from a pip requirements file.""" 8 | lineiter = (line.strip() for line in open(filename)) 9 | return [line for line in lineiter if line and not line.startswith("#")] 10 | 11 | 12 | if __name__ == '__main__': 13 | with open('README.md', 'r') as fh: 14 | LONG_DESCRIPTION = fh.read() 15 | 16 | setup( 17 | name='aries-acapy-protocol-example', 18 | version='0.1.0', 19 | author='Will Abramson , ', 20 | description='Aries Cloud Agent - Python Plugin Example', 21 | long_description=LONG_DESCRIPTION, 22 | long_description_content_type='text/markdown', 23 | # url='https://github.com/sovrin-foundation/aca-plugin-toolbox', 24 | license='Apache 2.0', 25 | packages=find_packages(), 26 | install_requires=parse_requirements('requirements.txt'), 27 | python_requires='>=3.6', 28 | classifiers=[ 29 | 'Programming Language :: Python :: 3', 30 | 'License :: OSI Approved :: Apache Software License', 31 | 'Operating System :: OS Independent' 32 | ] 33 | ) 34 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .code 3 | dist 4 | *.egg-info 5 | build 6 | .env 7 | .ipynb_checkpoints/ 8 | .venv -------------------------------------------------------------------------------- /libs/aries-basic-controller/Dockerfile.setup: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | ADD aries_basic_controller aries_basic_controller 4 | ADD requirements.txt . 5 | ADD setup.py . 6 | ADD README.md . 7 | 8 | RUN pip3 install --no-cache-dir -e . 9 | 10 | ADD tutorial/setup/create_connection.py . 11 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/README.md: -------------------------------------------------------------------------------- 1 | # Aries Basic Controller Python 2 | 3 | A simple pip installable package for controlling aries agents through admin API calls. 4 | 5 | # Install 6 | 7 | Current version 0.3 8 | 9 | `python3 -m pip install aries_basic_controller` 10 | 11 | 12 | ## Tutorial 13 | 14 | Find out how to use this package by taking our [jupyter notebook tutorial series](../../tutorials/). 15 | 16 | 17 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/aries-basic-controller/__init__.py -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/__init__.py: -------------------------------------------------------------------------------- 1 | from aries_basic_controller.aries_controller import AriesAgentController -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/aries_controller.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | from .aries_controller_base import AriesAgentControllerBase 4 | from .aries_webhook_server import AriesWebhookServer 5 | from .controllers.multitenant import MultitenancyController 6 | 7 | import logging 8 | 9 | logger = logging.getLogger("aries_controller") 10 | 11 | 12 | @dataclass 13 | class AriesAgentController(AriesAgentControllerBase): 14 | """The Aries Agent Controller class 15 | 16 | This class allows you to interact with Aries by exposing the aca-py API. 17 | 18 | Attributes: 19 | ---------- 20 | is_multitenant : bool 21 | Initialise the multitenant interface (default is False) 22 | """ 23 | 24 | is_multitenant: bool = False 25 | 26 | def __post_init__(self): 27 | """Constructs additional attributes and logic 28 | Creates headers, instantiates a client sessions and initialises 29 | the controller interfaces for the aries swagger API. 30 | """ 31 | 32 | super().__post_init__() 33 | 34 | if self.is_multitenant: 35 | self.multitenant = MultitenancyController( 36 | self.admin_url, 37 | self.client_session) 38 | 39 | def init_webhook_server( 40 | self, 41 | webhook_host: str = None, 42 | webhook_port: int = None, 43 | webhook_base: str = ""): 44 | """Create a webhooklisteners 45 | 46 | Args: 47 | ---- 48 | webhook_host : str 49 | The url of the webhook host (default is None) 50 | webhook_port : int 51 | The exposed port for webhooks on the host (default is None) 52 | webhook_base : str 53 | The base url for webhooks (default is "") 54 | """ 55 | self.webhook_server: AriesWebhookServer = AriesWebhookServer( 56 | webhook_host=webhook_host, 57 | webhook_port=webhook_port, 58 | webhook_base=webhook_base, 59 | is_multitenant=self.is_multitenant) 60 | 61 | async def listen_webhooks(self): 62 | try: 63 | await self.webhook_server.listen_webhooks() 64 | logger.info("Webhook server started.") 65 | except AttributeError: 66 | logger.warning("Webhook server not initialised.") 67 | except Exception as exc: 68 | logger.warning( 69 | f"Listening webhooks failed! {exc!r} occurred.") 70 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/aries_tenant_controller.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from pubsub import pub 3 | 4 | from .aries_controller_base import AriesAgentControllerBase 5 | 6 | import logging 7 | 8 | logger = logging.getLogger("aries_tenant_controller") 9 | 10 | _required = object() 11 | 12 | 13 | @dataclass 14 | class AriesTenantController(AriesAgentControllerBase): 15 | """The Aries Agent Controller class 16 | 17 | This class allows you to interact with Aries by exposing the aca-py API. 18 | 19 | Attributes: 20 | ---------- 21 | wallet_id : str 22 | The tenant wallet identifier 23 | tenant_jwt : str 24 | The tenant JW token 25 | """ 26 | 27 | wallet_id: str = _required 28 | tenant_jwt: str = _required 29 | 30 | def __post_init__(self): 31 | """Constructs additional attributes, 32 | and logic defined by attributes set during instantiation 33 | """ 34 | 35 | if (self.wallet_id is _required): 36 | raise TypeError("__init__ missing required wallet_id (str)") 37 | 38 | if (self.tenant_jwt is _required): 39 | raise TypeError("__init__ missing required tenant_jwt (str)") 40 | 41 | super().__post_init__() 42 | 43 | if self.tenant_jwt: 44 | self.headers.update( 45 | {'Authorization': 'Bearer ' + self.tenant_jwt, 46 | 'content-type': "application/json"}) 47 | 48 | # Update the current client session instantiated in the parent class 49 | self.client_session.headers.update(self.headers) 50 | 51 | def init_webhook_server(self): 52 | raise NotImplementedError( 53 | ("Please, use an AriesAgentController to start a webhook server\n" 54 | "Webhook server fct is disallowed for tenant controllers.")) 55 | 56 | def listen_webhooks(self): 57 | raise NotImplementedError( 58 | ("Please, use an AriesAgentController to start a webhook server\n" 59 | "Webhook server fct is disallowed for tenant controllers.")) 60 | 61 | def add_listener(self, listener): 62 | """Subscribe to a listeners for a topic 63 | Overrides parent method and uses the tenant's wallet ID to 64 | listen for that wallet's webhooks under the url defined 65 | by the wallet ID. 66 | 67 | Args: 68 | ---- 69 | listener : dict 70 | A dictionary comprised of "handler": handler (fct) and 71 | "topic":"topicname" key-value pairs 72 | """ 73 | try: 74 | pub_topic_path_base = listener['topic'] 75 | pub_topic_path = f"{self.wallet_id}.{pub_topic_path_base}" 76 | pub.subscribe(listener["handler"], pub_topic_path) 77 | logger.debug("Lister added for topic : ", pub_topic_path) 78 | except self.wallet_id == "": 79 | logger.error( 80 | "Cannot add listener for empty wallet_id.") 81 | except Exception as exc: 82 | logger.warning( 83 | f"Adding webhooks listener failed! {exc!r} occurred.") 84 | 85 | def update_wallet_id(self, wallet_id: str): 86 | """This wallet_id is used to register for webhooks 87 | specific to this sub_wallet 88 | 89 | Args: 90 | ---- 91 | wallet_id : str 92 | The tenant wallet identifier 93 | """ 94 | try: 95 | self.wallet_id = wallet_id 96 | except wallet_id == "": 97 | raise Exception("wallet_id must not be empty") 98 | 99 | def update_tenant_jwt(self, tenant_jwt: str, wallet_id: str): 100 | """Update the tenant JW token attribute and the header 101 | 102 | Args: 103 | ---- 104 | tenant_jwt : str 105 | The tenant's JW token 106 | wallet_id : str 107 | The tenant wallet identifier 108 | """ 109 | try: 110 | self.tenant_jwt = tenant_jwt 111 | self.update_wallet_id(wallet_id) 112 | self.headers.update( 113 | {'Authorization': 'Bearer ' + tenant_jwt, 114 | 'content-type': "application/json"}) 115 | self.client_session.headers.update(self.headers) 116 | except tenant_jwt == "": 117 | raise Exception("tenant_jwt must not be empty") 118 | except Exception as exc: 119 | logger.warning( 120 | (f"Updating tenant JW token" 121 | f" failed! {exc!r} occurred.")) 122 | 123 | def remove_tenant_jwt(self): 124 | """Removes the tenant's JW Token attribute and corresponding 125 | headers from the Client Session""" 126 | try: 127 | self.tenant_jwt = None 128 | if 'Authorization' in self.client_session.headers: 129 | del self.client_session.headers['Authorization'] 130 | del self.headers['Authorization'] 131 | if 'content-type' in self.client_session.headers: 132 | del self.client_session.headers['content-type'] 133 | del self.headers['content-type'] 134 | except Exception as exc: 135 | logger.warning( 136 | f"Removing JW token failed! {exc!r} occurred.") 137 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/aries_webhook_server.py: -------------------------------------------------------------------------------- 1 | from aiohttp import ( 2 | web, 3 | ClientRequest, 4 | ) 5 | from dataclasses import dataclass 6 | from pubsub import pub 7 | import logging 8 | 9 | logger = logging.getLogger("aries_webhook_server") 10 | 11 | 12 | @dataclass 13 | class AriesWebhookServer: 14 | """The Aries Webhook Listener class 15 | 16 | This class allows you to interact with Aries by exposing the aca-py API. 17 | 18 | Attributes: 19 | ---------- 20 | webhook_host : str 21 | The url of the webhook host 22 | webhook_port : int 23 | The exposed port for webhooks on the host 24 | webhook_base : str 25 | The base url for webhooks (default is "") 26 | is_multitenant : bool 27 | Handles a multitenant setup (default is False) 28 | """ 29 | 30 | webhook_host: str 31 | webhook_port: int 32 | webhook_base: str = "" 33 | is_multitenant: bool = False 34 | 35 | async def listen_webhooks(self): 36 | """Create a server to listen to webhooks""" 37 | try: 38 | app = web.Application() 39 | app.add_routes([web.post( 40 | self.webhook_base + "/topic/{topic}/", 41 | self._receive_webhook)]) 42 | if self.is_multitenant: 43 | app.add_routes( 44 | [web.post( 45 | self.webhook_base + "/{wallet_id}/topic/{topic}/", 46 | self._receive_webhook)]) 47 | runner = web.AppRunner(app) 48 | await runner.setup() 49 | self.webhook_site = web.TCPSite( 50 | runner, 51 | self.webhook_host, 52 | self.webhook_port) 53 | await self.webhook_site.start() 54 | except Exception as exc: 55 | logger.warning(f"Listening webhooks failed! {exc!r} occurred.") 56 | 57 | async def _receive_webhook(self, request: ClientRequest): 58 | """Helper to receive webhooks by requesting it 59 | 60 | Args: 61 | ---- 62 | request : ClientRequest 63 | The client request to which the corresponding webhooks 64 | shall be received 65 | 66 | Returns: 67 | ------- 68 | Response: 69 | A response with status 200 70 | """ 71 | topic = request.match_info["topic"] 72 | wallet_id = None 73 | if self.is_multitenant: 74 | wallet_id = request.match_info["wallet_id"] 75 | logger.info(f"Received webhook from wallet id: {wallet_id}") 76 | try: 77 | payload = await request.json() 78 | await self._handle_webhook(wallet_id, topic, payload) 79 | return web.Response(status=200) 80 | except Exception as exc: 81 | logger.warning(f"Receiving webhooks failed! {exc!r} occurred.") 82 | 83 | async def _handle_webhook(self, wallet_id, topic, payload): 84 | """Helper handling a webhook 85 | 86 | Args: 87 | ---- 88 | wallet_id: str 89 | The identifier for the wallet the webhook is for 90 | topic : str 91 | The topic to handle webhooks for 92 | payload : dict 93 | A JSON-like dictionary representation of the payload 94 | """ 95 | try: 96 | pub_topic_path = topic 97 | if wallet_id: 98 | pub_topic_path = f"{wallet_id}.{topic}" 99 | logging.debug(f"Handle Webhook - {pub_topic_path}", payload) 100 | pub.sendMessage(pub_topic_path, payload=payload) 101 | # return web.Response(status=200) 102 | except Exception as exc: 103 | logger.warning( 104 | (f"Handling webhooks failed! {exc!r} occurred" 105 | f" when trying to handle this topic: {topic}")) 106 | 107 | async def terminate(self): 108 | """Terminate the controller client session and webhook listeners""" 109 | try: 110 | await self.webhook_site.stop() 111 | except AttributeError: 112 | # Do nothing if no webhook site server is running 113 | return 114 | except Exception as exc: 115 | logger.warning( 116 | f"Terminating webhooks listener failed! {exc!r} occurred.") 117 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/aries-basic-controller/aries_basic_controller/controllers/__init__.py -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/action_menu.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from ..models.connection import Connection 3 | 4 | from aiohttp import ( 5 | ClientSession, 6 | ) 7 | import logging 8 | import asyncio 9 | 10 | class ActionMenuController(BaseController): 11 | 12 | def __init__(self, admin_url: str, client_session: ClientSession): 13 | super().__init__(admin_url, client_session) 14 | 15 | async def close_menu(self, connection_id: str): 16 | return await self.admin_POST(f"/action-menu/{connection_id}/close") 17 | 18 | async def get_menu(self, connection_id: str): 19 | return await self.admin_POST(f"/action-menu/{connection_id}/fetch") 20 | 21 | async def request_menu(self, connection_id: str): 22 | return await self.admin_POST(f"/action-menu/{connection_id}/request") 23 | 24 | async def perform(self, connection_id: str, menu_params, menu_option_name): 25 | body = { 26 | "params": menu_params, 27 | "name": menu_option_name 28 | } 29 | 30 | return await self.admin_POST(f"/action-menu/{connection_id}/perform", json_data=body) 31 | 32 | async def send_menu( 33 | self, 34 | connection_id: str, 35 | menu_description: str, 36 | menu_errormsg: str, 37 | menu_title: str, 38 | menu_options, 39 | ): 40 | body = { 41 | "menu": { 42 | "description": menu_description, 43 | "errormsg": menu_errormsg, 44 | "title": menu_title, 45 | "options": menu_options 46 | } 47 | } 48 | 49 | return await self.admin_POST(f"/connections/{connection_id}/send-menu", json_data=body) -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/credential.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from aiohttp import ClientSession 3 | import logging 4 | 5 | logger = logging.getLogger("aries_controller.credentials") 6 | 7 | class CredentialController(BaseController): 8 | 9 | def __init__(self, admin_url: str, client_session: ClientSession): 10 | super().__init__(admin_url, client_session) 11 | 12 | async def get_by_id(self, credential_id): 13 | return await self.admin_GET(f"/credential/{credential_id}") 14 | 15 | async def get_credential_mime_types(self, credential_id): 16 | return await self.admin_GET(f"/credential/mime-types/{credential_id}") 17 | 18 | async def remove_credential(self, credential_id): 19 | return await self.admin_DELETE(f"/credential/{credential_id}") 20 | 21 | async def get_all(self, wql_query: str = None, count: int = None, start: int = None): 22 | params = {} 23 | if wql_query: 24 | params["wql"] = wql_query 25 | if count: 26 | params["count"] = count 27 | if start: 28 | params["start"] = start 29 | 30 | return await self.admin_GET("/credentials", params=params) 31 | 32 | async def is_revoked(self, credential_id): 33 | return await self.admin_GET(f"/credential/revoked/{credential_id}") 34 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/definitions.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from aiohttp import ClientSession 3 | import logging 4 | 5 | logger = logging.getLogger("aries_controller.definitions") 6 | 7 | class DefinitionsController(BaseController): 8 | 9 | def __init__(self, admin_url: str, client_session: ClientSession): 10 | super().__init__(admin_url, client_session) 11 | self.base_url = "/credential-definitions" 12 | 13 | async def get_by_id(self, cred_def_id): 14 | return await self.admin_GET(f"{self.base_url}/{cred_def_id}") 15 | 16 | async def write_cred_def(self, schema_id, tag: str = "default", support_revocation: bool = False): 17 | 18 | body = { 19 | "schema_id": schema_id, 20 | "tag": tag, 21 | "support_revocation": support_revocation 22 | } 23 | 24 | return await self.admin_POST(f"{self.base_url}", body) 25 | 26 | async def search_created(self, schema_id = None, schema_issuer_did=None, schema_name=None, 27 | schema_version=None, issuer_did=None, cred_def_id=None): 28 | 29 | params = {} 30 | if schema_id: 31 | params["schema_id"] = schema_id 32 | if schema_issuer_did: 33 | params["schema_issuer_did"] = schema_issuer_did 34 | if schema_version: 35 | params["schema_version"] = schema_version 36 | if schema_name: 37 | params["schema_name"] = schema_name 38 | if issuer_did: 39 | params["issuer_did"] = issuer_did 40 | if cred_def_id: 41 | params["cred_def_id"] = cred_def_id 42 | 43 | return await self.admin_GET(f"{self.base_url}/created", params=params) 44 | 45 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/ledger.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from aiohttp import ClientSession 3 | import logging 4 | 5 | logger = logging.getLogger("aries_controller.ledger") 6 | 7 | class LedgerController(BaseController): 8 | 9 | def __init__(self, admin_url: str, client_session: ClientSession): 10 | super().__init__(admin_url, client_session) 11 | self.base_url = "/ledger" 12 | 13 | async def register_nym(self, did, verkey, role: str = None, alias: str = None): 14 | params = { 15 | "did": did, 16 | "verkey": verkey 17 | } 18 | if role: 19 | params["role"] = role 20 | if alias: 21 | params["alias"] = alias 22 | 23 | return await self.admin_POST(f"{self.base_url}/register-nym", params=params) 24 | 25 | async def get_nym_role(self, did): 26 | params = { 27 | "did": did 28 | } 29 | return await self.admin_GET(f"{self.base_url}/get-nym-role", params=params) 30 | 31 | 32 | async def get_did_verkey(self, did): 33 | params = { 34 | "did": did 35 | } 36 | return await self.admin_GET(f"{self.base_url}/did-verkey", params=params) 37 | 38 | async def get_did_endpoint(self, did): 39 | params = { 40 | "did": did 41 | } 42 | return await self.admin_GET(f"{self.base_url}/did-endpoint", params=params) 43 | 44 | async def get_taa(self): 45 | return await self.admin_GET(f"{self.base_url}/taa") 46 | 47 | async def accept_taa(self, data): 48 | return await self.admin_POST(f"{self.base_url}/taa/accept", json_data=data) 49 | 50 | #TODO PATCH rotate key pair 51 | 52 | 53 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/mediation.py: -------------------------------------------------------------------------------- 1 | # For usage please consult https://github.com/hyperledger/aries-cloudagent-python/blob/main/Mediation.md 2 | 3 | from .base import BaseController 4 | from aiohttp import ClientSession 5 | import logging 6 | from typing import List 7 | 8 | logger = logging.getLogger("aries_controller.mediation") 9 | 10 | 11 | class MediationController(BaseController): 12 | 13 | def __init__(self, admin_url: str, client_session: ClientSession): 14 | super().__init__(admin_url, client_session) 15 | self.base_url = "/mediation" 16 | 17 | def default_handler(self, payload): 18 | logger.debug("Mediation Message received", payload) 19 | 20 | 21 | # Get default mediator 22 | async def get_default_mediator(self): 23 | return await self.admin_GET(f"{self.base_url}/default-mediator") 24 | 25 | 26 | # Clear default mediator 27 | async def delete_default_mediator(self): 28 | return await self.admin_DELETE(f"{self.base_url}/default-mediator") 29 | 30 | 31 | # Retrieve keylists by connection or role 32 | async def get_keylists(self, conn_id: str = None, role: str = None): 33 | params = {} 34 | if conn_id: 35 | params["conn_id"] = conn_id 36 | if role: 37 | params["role"] = role 38 | 39 | return await self.admin_GET(f"{self.base_url}/keylists", params=params) 40 | 41 | 42 | # Send keylist query to mediator 43 | async def send_keylist_query(self, request, mediation_id: str, paginate_limit: int = -1, paginate_offset: int = 0): 44 | params = {} 45 | params["mediation_id"] = mediation_id 46 | if paginate_limit: 47 | params["paginate_limit"] = paginate_limit 48 | if paginate_offset: 49 | params["paginate_offset"] = paginate_offset 50 | 51 | return await self.admin_POST(f"{self.base_url}/keylists/{mediation_id}/send-keylist-query", params=params, json_data=request) 52 | 53 | 54 | # Send keylist update to mediator 55 | async def send_keylist_update(self, request, mediation_id: str): 56 | return await self.admin_POST(f"{self.base_url}/keylists/{mediation_id}/send-keylist-update", json_data=request) 57 | 58 | 59 | # Request mediation from connection 60 | async def request_mediation(self, conn_id: str, request: {} = {}): 61 | return await self.admin_POST(f"{self.base_url}/request/{conn_id}", json_data=request) 62 | 63 | 64 | # Query mediation requests, returns list of all mediation records 65 | async def get_mediation_records(self, mediator_terms: [str] = None, recipient_terms: [str] = None, state: str = None, conn_id: str = None): 66 | params = {} 67 | if conn_id: 68 | params["conn_id"] = conn_id 69 | if mediator_terms: 70 | params["mediator_terms"] = mediator_terms 71 | if recipient_terms: 72 | params["recipient_terms"] = recipient_terms 73 | if state: 74 | params["state"] = state 75 | 76 | return await self.admin_GET(f"{self.base_url}/requests", params=params) 77 | 78 | 79 | # Retrieve mediation request record 80 | async def get_mediation_record_by_id(self, mediation_id: str): 81 | return await self.admin_GET(f"{self.base_url}/requests/{mediation_id}") 82 | 83 | 84 | # Delete mediation request record 85 | async def delete_mediation_record_by_id(self, mediation_id: str): 86 | return await self.admin_DELETE(f"{self.base_url}/requests/{mediation_id}") 87 | 88 | 89 | # Deny a stored mediation request 90 | async def deny_mediation_request_by_id(self, request, mediation_id: str): 91 | return await self.admin_POST(f"{self.base_url}/requests/{mediation_id}/deny", json_data=request) 92 | 93 | 94 | # Grant received mediation request 95 | async def grant_mediation_request_by_id(self, mediation_id: str): 96 | return await self.admin_POST(f"{self.base_url}/requests/{mediation_id}/grant") 97 | 98 | 99 | # Set default mediator 100 | async def set_default_mediator(self, mediation_id: str): 101 | return await self.admin_PUT(f"{self.base_url}/{mediation_id}/default-mediator") 102 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/messaging.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from aiohttp import ClientSession 3 | import logging 4 | 5 | logger = logging.getLogger("aries_controller.messaging") 6 | 7 | class MessagingController(BaseController): 8 | 9 | def __init__(self, admin_url: str, client_session: ClientSession): 10 | super().__init__(admin_url, client_session) 11 | 12 | 13 | def default_handler(self, payload): 14 | logger.debug("Message Received ", payload) 15 | 16 | async def send_message(self, connection_id, msg): 17 | response = await self.admin_POST(f"/connections/{connection_id}/send-message", { 18 | "content": msg, 19 | }) 20 | return response 21 | 22 | async def trust_ping(self, connection_id: str, msg: str): 23 | response = await self.admin_POST(f"/connections/{connection_id}/send-ping", {"content": msg}) 24 | return response 25 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/multitenant.py: -------------------------------------------------------------------------------- 1 | # For usage please consult https://github.com/hyperledger/aries-cloudagent-python/blob/main/Mediation.md 2 | 3 | from .base import BaseController 4 | from aiohttp import ClientSession 5 | import logging 6 | from typing import List 7 | 8 | logger = logging.getLogger("aries_controller.multitenancy") 9 | 10 | 11 | class MultitenancyController(BaseController): 12 | 13 | def __init__(self, admin_url: str, client_session: ClientSession): 14 | super().__init__(admin_url, client_session) 15 | self.base_url = "/multitenancy" 16 | 17 | def default_handler(self, payload): 18 | logger.debug("Multitenancy Message received", payload) 19 | 20 | 21 | # Create a subwallet 22 | async def create_subwallet(self, request): 23 | return await self.admin_POST(f"{self.base_url}/wallet", json_data=request) 24 | 25 | 26 | # Get a single subwallet 27 | async def get_single_subwallet_by_id(self, wallet_id: str): 28 | return await self.admin_GET(f"{self.base_url}/wallet/{wallet_id}") 29 | 30 | 31 | # Update a subwallet 32 | async def update_subwallet_by_id(self, request, wallet_id: str): 33 | return await self.admin_PUT(f"{self.base_url}/wallet/{wallet_id}", json_data=request) 34 | 35 | 36 | # Remove a subwallet 37 | async def remove_subwallet_by_id(self, wallet_id: str, request=None): 38 | return await self.admin_POST(f"{self.base_url}/wallet/{wallet_id}/remove", json_data=request) 39 | 40 | 41 | # Get auth token for a subwallet 42 | async def get_subwallet_authtoken_by_id(self, wallet_id: str, request=None): 43 | return await self.admin_POST(f"{self.base_url}/wallet/{wallet_id}/token", json_data=request) 44 | 45 | 46 | # Query subwallets 47 | async def query_subwallets(self, wallet_name: str = None): 48 | params = {} 49 | if wallet_name: 50 | params["wallet_name"] = wallet_name 51 | 52 | return await self.admin_GET(f"{self.base_url}/wallets") 53 | 54 | 55 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/oob.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from aiohttp import ClientSession 3 | import logging 4 | 5 | logger = logging.getLogger("aries_controller.out-of-band") 6 | 7 | class OOBController(BaseController): 8 | 9 | def __init__(self, admin_url: str, client_session: ClientSession): 10 | super().__init__(admin_url, client_session) 11 | 12 | 13 | def default_handler(self, payload): 14 | logger.debug("Out of Band ", payload) 15 | 16 | async def create_invitation(self, data): 17 | response = await self.admin_POST(f"/out-of-band/create-invitation", json_data = data) 18 | return response 19 | 20 | async def receive_invitation(self, data): 21 | response = await self.admin_POST(f"/out-of-band/receive-invitation", json_data = data) 22 | return response -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/proof.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from aiohttp import ClientSession 3 | import logging 4 | from typing import List 5 | 6 | logger = logging.getLogger("aries_controller.proof") 7 | 8 | PRES_PREVIEW = "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/present-proof/1.0/presentation-preview" 9 | 10 | class ProofController(BaseController): 11 | 12 | def __init__(self, admin_url: str, client_session: ClientSession): 13 | super().__init__(admin_url, client_session) 14 | self.base_url = "/present-proof" 15 | 16 | def default_handler(self, payload): 17 | logger.debug("Present Proof Message received", payload) 18 | 19 | 20 | # Creates a presentation request not bound to any proposal or existing connection 21 | async def create_request(self, request): 22 | # TODO How should proof request object be broken up? Complex. 23 | # Do we want user to have to know how to build this object? 24 | return await self.admin_POST(f"{self.base_url}/create-request", json_data=request) 25 | 26 | 27 | # Fetch all present-proof exchange records 28 | async def get_records(self, connection_id: str = None, thread_id: str = None, state: str = None, role: str = None): 29 | params = {} 30 | if connection_id: 31 | params["connection_id"] = connection_id 32 | if thread_id: 33 | params["thread_id"] = thread_id 34 | if state: 35 | params["state"] = state 36 | if role: 37 | params["role"] = role 38 | 39 | return await self.admin_GET(f"{self.base_url}/records", params=params) 40 | 41 | 42 | # Fetch a single presentation exchange record 43 | async def get_record_by_id(self, pres_ex_id): 44 | return await self.admin_GET(f"{self.base_url}/records/{pres_ex_id}") 45 | 46 | 47 | # Remove an existing presentation exchange record 48 | async def remove_presentation_record(self, pres_ex_id): 49 | return await self.admin_DELETE(f"{self.base_url}/records/{pres_ex_id}") 50 | 51 | 52 | # Fetch credentials for a presentation request from wallet 53 | async def get_presentation_credentials(self, pres_ex_id, count: int = None, wql_query: str = None, start: int = None, referent: str = None): 54 | params = {} 55 | if count: 56 | params["count"] = count 57 | # Not sure what this does 58 | if wql_query: 59 | params["extra_query"] = wql_query 60 | if start: 61 | params["start"] = start 62 | # Not sure what this does 63 | if referent: 64 | params["referent"] = referent 65 | 66 | return await self.admin_GET(f"{self.base_url}/records/{pres_ex_id}/credentials", params=params) 67 | 68 | 69 | # Send a problem report for presentation exchange 70 | async def send_problem_report(self, pres_ex_id, request): 71 | return await self.admin_POST(f"{self.base_url}/records/{pres_ex_id}/problem-report", json_data=request) 72 | 73 | 74 | # Sends a proof presentation 75 | async def send_presentation(self, pres_ex_id, request): 76 | return await self.admin_POST(f"{self.base_url}/records/{pres_ex_id}/send-presentation", json_data=request) 77 | 78 | 79 | # Sends a presentation request in reference to a proposal 80 | async def send_request_for_proposal(self, pres_ex_id, request): 81 | return await self.admin_POST(f"{self.base_url}/records/{pres_ex_id}/send-request", json_data=request) 82 | 83 | 84 | # Verify a received presentation 85 | async def verify_presentation(self, pres_ex_id): 86 | return await self.admin_POST(f"{self.base_url}/records/{pres_ex_id}/verify-presentation") 87 | 88 | 89 | # Sends a presentation proposal 90 | async def send_proposal(self, request): 91 | return await self.admin_POST(f"{self.base_url}/send-proposal", json_data=request) 92 | 93 | 94 | # Sends a free presentation request not bound to any proposal 95 | async def send_request(self, request): 96 | return await self.admin_POST(f"{self.base_url}/send-request", json_data=request) 97 | 98 | 99 | # def build_proof_request(self, name, version, requested_attributes, requested_predicates): 100 | 101 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/schema.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from aiohttp import ClientSession 3 | import logging 4 | 5 | logger = logging.getLogger("aries_controller.schema") 6 | 7 | 8 | class SchemaController(BaseController): 9 | 10 | def __init__(self, admin_url: str, client_session: ClientSession): 11 | super().__init__(admin_url, client_session) 12 | self.base_url = "/schemas" 13 | 14 | async def get_by_id(self, schema_id): 15 | 16 | response = await self.admin_GET(f"{self.base_url}/{schema_id}") 17 | return response 18 | 19 | async def get_created_schema(self, schema_id=None, schema_issuer_did=None, schema_name=None, schema_version=None): 20 | 21 | params = {} 22 | if schema_id: 23 | params["schema_id"] = schema_id 24 | if schema_issuer_did: 25 | params["schema_issuer_did"] = schema_issuer_did 26 | if schema_name: 27 | params["schema_name"] = schema_name 28 | if schema_version: 29 | params["schema_version"] = schema_version 30 | 31 | response = await self.admin_GET(f"{self.base_url}/created", params=params) 32 | 33 | return response 34 | 35 | async def write_schema(self, schema_name, attributes, schema_version): 36 | 37 | schema_body = { 38 | "schema_name": schema_name, 39 | "attributes": attributes, 40 | "schema_version": schema_version 41 | } 42 | 43 | response = await self.admin_POST(f"{self.base_url}", schema_body) 44 | return response 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/server.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from aiohttp import ClientSession 3 | import logging 4 | 5 | logger = logging.getLogger("aries_controller.proof") 6 | 7 | 8 | class ServerController(BaseController): 9 | 10 | def __init__(self, admin_url: str, client_session: ClientSession): 11 | super().__init__(admin_url, client_session) 12 | 13 | async def get_plugins(self): 14 | return await self.admin_GET('/plugins') 15 | 16 | async def get_status(self): 17 | return await self.admin_GET('/status') 18 | 19 | async def reset_status(self): 20 | return await self.admin_POST('/status/reset') 21 | 22 | async def status_ready(self): 23 | return await self.admin_GET('/status/ready') 24 | 25 | async def status_live(self): 26 | return await self.admin_GET('/status/live') 27 | 28 | async def get_features(self, query: str = None): 29 | params = {} 30 | if query: 31 | params["query"] = query 32 | 33 | return await self.admin_GET('/features', params=params) 34 | 35 | ## TODO implement shutdown. Should this be a POST? -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/controllers/wallet.py: -------------------------------------------------------------------------------- 1 | from .base import BaseController 2 | from aiohttp import ClientSession 3 | import logging 4 | 5 | logger = logging.getLogger("aries_controller.wallet") 6 | 7 | class WalletController(BaseController): 8 | 9 | def __init__(self, admin_url: str, client_session: ClientSession): 10 | super().__init__(admin_url, client_session) 11 | self.base_url = "/wallet" 12 | 13 | async def get_dids(self): 14 | return await self.admin_GET(f"{self.base_url}/did") 15 | 16 | 17 | async def create_did(self): 18 | return await self.admin_POST(f"{self.base_url}/did/create") 19 | 20 | async def get_public_did(self): 21 | return await self.admin_GET(f"{self.base_url}/did/public") 22 | 23 | async def assign_public_did(self, did): 24 | params = { 25 | "did": did 26 | } 27 | return await self.admin_POST(f"{self.base_url}/did/public", params=params) 28 | 29 | async def get_did_endpoint(self, did): 30 | params = { 31 | "did": did 32 | } 33 | return await self.admin_GET(f"{self.base_url}/get-did-endpoint", params=params) 34 | 35 | async def set_did_endpoint(self, did, endpoint, endpoint_type): 36 | body = { 37 | "did": did, 38 | "endpoint": endpoint, 39 | "endpoint_type": endpoint_type 40 | } 41 | return await self.admin_POST(f"{self.base_url}/set-did-endpoint", json_data=body) 42 | 43 | ## TODO Patch rotate-keypair -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/aries-basic-controller/aries_basic_controller/helpers/__init__.py -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/aries-basic-controller/aries_basic_controller/models/__init__.py -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/models/connection.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | class Connection: 4 | 5 | def __init__(self, id, state): 6 | self.id = id 7 | self.state = state 8 | self.future_state = None 9 | self.future = asyncio.Future() 10 | 11 | async def detect_state_ready(self, future_state): 12 | if self.state == future_state: 13 | return True 14 | else: 15 | self.future_state = future_state 16 | await self.future 17 | 18 | def update_state(self, state): 19 | self.state = state 20 | if state == self.future_state: 21 | self.future.set_result(True) 22 | 23 | @property 24 | def connection_ready(self): 25 | return self.state == "active" -------------------------------------------------------------------------------- /libs/aries-basic-controller/aries_basic_controller/models/errors.py: -------------------------------------------------------------------------------- 1 | class Error(Exception): 2 | """Base class for exceptions in this module.""" 3 | pass 4 | 5 | 6 | class InputError(Error): 7 | """Exception raised for errors in the input. 8 | 9 | Attributes: 10 | message -- explanation of the error 11 | """ 12 | 13 | def __init__(self, message): 14 | self.message = message 15 | -------------------------------------------------------------------------------- /libs/aries-basic-controller/build.sh: -------------------------------------------------------------------------------- 1 | python3 setup.py sdist bdist_wheel -------------------------------------------------------------------------------- /libs/aries-basic-controller/requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp~=3.7.3 2 | asyncio 3 | prompt_toolkit 4 | pygments 5 | PyPubSub 6 | python-dotenv 7 | termcolor 8 | pillow 9 | qrcode 10 | beautifulsoup4 -------------------------------------------------------------------------------- /libs/aries-basic-controller/setup.py: -------------------------------------------------------------------------------- 1 | """Module setup.""" 2 | 3 | import os 4 | import runpy 5 | from setuptools import setup, find_packages 6 | 7 | PACKAGE_NAME = "aries_basic_controller" 8 | # version_meta = runpy.run_path("./{}/version.py".format(PACKAGE_NAME)) 9 | # VERSION = version_meta["__version__"] 10 | 11 | 12 | with open(os.path.abspath("./README.md"), "r") as fh: 13 | long_description = fh.read() 14 | 15 | 16 | def parse_requirements(filename): 17 | """Load requirements from a pip requirements file.""" 18 | lineiter = (line.strip() for line in open(filename)) 19 | return [line for line in lineiter if line and not line.startswith("#")] 20 | 21 | 22 | if __name__ == "__main__": 23 | setup( 24 | name=PACKAGE_NAME, 25 | version="0.3", 26 | author="Will Abramson", 27 | description="A simple python package for controlling an aries agent through the admin-api interface", 28 | long_description=long_description, 29 | long_description_content_type="text/markdown", 30 | url="https://github.com/OpenMined/PyDentity/tree/master/libs/aries-basic-controller", 31 | packages=find_packages(), 32 | include_package_data=True, 33 | package_data={"aries_basic_controller": ["requirements.txt"]}, 34 | install_requires=parse_requirements("requirements.txt"), 35 | python_requires=">=3.7.3", 36 | classifiers=[ 37 | "Programming Language :: Python :: 3", 38 | "License :: OSI Approved :: Apache Software License", 39 | "Operating System :: OS Independent", 40 | ], 41 | ) -------------------------------------------------------------------------------- /libs/attachment-controller/README.md: -------------------------------------------------------------------------------- 1 | # Aries Basic Controller Python 2 | 3 | A simple pip installable package for controlling aries agents through admin API calls. 4 | 5 | # Install 6 | 7 | Package only available on the test at the moment. 8 | 9 | `python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps aries_basic_controller` 10 | 11 | 12 | # Demo 13 | 14 | With docker installed, run the example using ./manage start 15 | 16 | This spins up an aries agent and a notebook for both Alice and Bob. Additionally a local von-network is initialised. 17 | 18 | To view the tutorials navigate to: 19 | * [Alice notebook](http://localhost:8888) - this contains the majority of the tutorials 20 | * [Bob notebook](http://localhost:8889) - Bob plays the other half of the protocols when needed. 21 | 22 | Both notebooks require a token that can be found in the logs. Fetch the logs for the relevant container using these commands: 23 | * Alice : `docker logs om-aries-controller_alice-notebook_1` 24 | * Bob : `docker logs om-aries-controller_bob-notebook_1` 25 | 26 | 27 | 28 | 29 | # Sequence Diagram 30 | 31 | Built using [Sequence Diagram](https://sequencediagram.org) 32 | 33 | ![Sequence Diagram](./sequence_diagrams/controller_basic_messaging.svg) -------------------------------------------------------------------------------- /libs/attachment-controller/attachment_controller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/attachment-controller/attachment_controller/__init__.py -------------------------------------------------------------------------------- /libs/attachment-controller/attachment_controller/attachment_controller.py: -------------------------------------------------------------------------------- 1 | from aries_basic_controller.aries_controller import AriesAgentController 2 | from .protocol_controller import ProtocolController 3 | 4 | 5 | class AttachmentController(AriesAgentController): 6 | 7 | def __init__(self, webhook_host: str, webhook_port: int, admin_url: str, webhook_base: str = "", 8 | connections: bool = True, messaging: bool = True, issuer: bool = True, api_key = None): 9 | super().__init__(webhook_host, webhook_port, admin_url, webhook_base, 10 | connections, messaging, issuer, api_key=api_key) 11 | 12 | self.protocol = ProtocolController(admin_url, self.client_session, self.connections) 13 | -------------------------------------------------------------------------------- /libs/attachment-controller/attachment_controller/protocol_controller.py: -------------------------------------------------------------------------------- 1 | from aries_basic_controller.controllers.base import BaseController 2 | from aries_basic_controller.controllers.connections import ConnectionsController 3 | 4 | from aiohttp import ClientSession 5 | 6 | 7 | # This is an example for how we might extend the basic controller 8 | class ProtocolController(BaseController): 9 | 10 | def __init__( 11 | self, admin_url: str, 12 | client_session: ClientSession, 13 | connections_controller: ConnectionsController): 14 | 15 | super().__init__(admin_url, client_session) 16 | self.connections = connections_controller 17 | 18 | async def send_attachment(self, connection_id, data): 19 | 20 | await self.connections.is_active(connection_id) 21 | 22 | path = f"/connections/{connection_id}/send-attachment" 23 | return await self.admin_POST(path, data=data) 24 | 25 | # async def store_attachment(self,data): 26 | -------------------------------------------------------------------------------- /libs/attachment-controller/requirements.txt: -------------------------------------------------------------------------------- 1 | aries-basic-controller<0.3 -------------------------------------------------------------------------------- /libs/attachment-controller/setup.py: -------------------------------------------------------------------------------- 1 | """Module setup.""" 2 | 3 | import os 4 | import runpy 5 | from setuptools import setup, find_packages 6 | 7 | PACKAGE_NAME = "attachment_controller" 8 | # version_meta = runpy.run_path("./{}/version.py".format(PACKAGE_NAME)) 9 | # VERSION = version_meta["__version__"] 10 | 11 | 12 | with open(os.path.abspath("./README.md"), "r") as fh: 13 | long_description = fh.read() 14 | 15 | 16 | def parse_requirements(filename): 17 | """Load requirements from a pip requirements file.""" 18 | lineiter = (line.strip() for line in open(filename)) 19 | return [line for line in lineiter if line and not line.startswith("#")] 20 | 21 | 22 | if __name__ == "__main__": 23 | setup( 24 | name=PACKAGE_NAME, 25 | version="0.0.1", 26 | author="Will Abramson", 27 | description="A python package for an OpenMined specific Aries Controller", 28 | long_description=long_description, 29 | long_description_content_type="text/markdown", 30 | # url="https://github.com/wip-abramson/aries-basic-controller-python", 31 | packages=find_packages(), 32 | include_package_data=True, 33 | package_data={"attachment_controller": ["requirements.txt"]}, 34 | install_requires=parse_requirements("requirements.txt"), 35 | python_requires=">=3.6.3", 36 | classifiers=[ 37 | "Programming Language :: Python :: 3", 38 | "License :: OSI Approved :: Apache Software License", 39 | "Operating System :: OS Independent", 40 | ], 41 | ) -------------------------------------------------------------------------------- /libs/attachment-protocol/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/attachment-protocol/README.md -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/attachment-protocol/attach_protocol/__init__.py -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/attachment_protocol/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/attachment-protocol/attach_protocol/attachment_protocol/__init__.py -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/attachment_protocol/definition.py: -------------------------------------------------------------------------------- 1 | """Version definitions for this protocol.""" 2 | 3 | versions = [ 4 | { 5 | "major_version": 1, 6 | "minimum_minor_version": 0, 7 | "current_minor_version": 0, 8 | "path": "v1_0", 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/attachment_protocol/v1_0/handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/attachment-protocol/attach_protocol/attachment_protocol/v1_0/handlers/__init__.py -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/attachment_protocol/v1_0/handlers/attachment_handler.py: -------------------------------------------------------------------------------- 1 | #Attachment Handler 2 | 3 | from aries_cloudagent.messaging.base_handler import ( 4 | BaseHandler, 5 | BaseResponder, 6 | RequestContext, 7 | ) 8 | 9 | from ..messages.attachment import Attachment 10 | 11 | class AttachmentHandler(BaseHandler): 12 | 13 | async def handle(self, context: RequestContext, responder: BaseResponder): 14 | 15 | self._logger.info(f"AttachmentHandler called") 16 | assert isinstance(context.message, Attachment) 17 | 18 | self._logger.info("Received attached message %s", context.message) 19 | 20 | attachment = context.message 21 | attachment_message = attachment.message 22 | attach_decorator = attachment.files_attach[0] 23 | filename = attach_decorator.filename 24 | content = attach_decorator.data.base64 25 | 26 | print("Filename", filename) 27 | print("Content", content) 28 | 29 | # meta ={"content": body} 30 | 31 | self._logger.info("Send Webhook with topic attached message") 32 | await responder.send_webhook( 33 | "attachment", 34 | { 35 | "connection_id": context.connection_record.connection_id, 36 | "message_id": context.message._id, 37 | "attachment_message": attachment_message, 38 | "content": content, 39 | "filename": filename, 40 | "state": "received", 41 | }, 42 | ) 43 | 44 | # reply = None 45 | # if body: 46 | # if context.settings.get("debug.auto_respond_messages"): 47 | # if "received your message" not in body: 48 | # reply = f"{context.default_label} received your message" 49 | # elif body.startswith("Reply with: "): 50 | # reply = body[12:] 51 | # 52 | # if reply: 53 | # reply_msg = Attachment(message=reply) 54 | # reply_msg.assign_thread_from(context.message) 55 | # reply_msg.assign_trace_from(context.message) 56 | # if "l10n" in context.message._decorators: 57 | # reply_msg._decorators["l10n"] = context.message._decorators["l10n"] 58 | # await responder.send_reply(reply_msg) 59 | -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/attachment_protocol/v1_0/message_types.py: -------------------------------------------------------------------------------- 1 | 2 | PROTOCOL_URI ="did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/attachmentprotocol/1.0" 3 | 4 | ATTACHMENT = f"{PROTOCOL_URI}/attachmentprotocol" 5 | 6 | NEW_PROTOCOL_URI ="https://didcomm.org/attachmentprotocol/1.0" 7 | 8 | NEW_ATTACHMENT_PROTOCOL = f"{NEW_PROTOCOL_URI}/attachmentprotocol" 9 | 10 | PROTOCOL_PACKAGE ="attach_protocol.attachment_protocol.v1_0" 11 | 12 | MESSAGE_TYPES = { 13 | ATTACHMENT: f"{PROTOCOL_PACKAGE}.messages.attachment.Attachment", 14 | NEW_ATTACHMENT_PROTOCOL: f"{PROTOCOL_PACKAGE}.messages.attachment.Attachment", 15 | } -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/attachment_protocol/v1_0/messages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/libs/attachment-protocol/attach_protocol/attachment_protocol/v1_0/messages/__init__.py -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/attachment_protocol/v1_0/messages/attachment.py: -------------------------------------------------------------------------------- 1 | """ Protocol for attachment message""" 2 | 3 | from ..message_types import PROTOCOL_PACKAGE, ATTACHMENT 4 | 5 | import uuid 6 | 7 | from typing import Sequence 8 | 9 | from aries_cloudagent.wallet.util import bytes_to_b64 10 | from aries_cloudagent.messaging.decorators.attach_decorator import ( 11 | AttachDecorator, 12 | AttachDecoratorSchema, 13 | AttachDecoratorData 14 | ) 15 | 16 | from marshmallow import fields 17 | 18 | from aries_cloudagent.messaging.agent_message import AgentMessage, AgentMessageSchema 19 | 20 | HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.attachment_handler.AttachmentHandler" 21 | 22 | class Attachment(AgentMessage): 23 | #Class representing message attachment 24 | 25 | class Meta: 26 | handler_class = HANDLER_CLASS 27 | message_type = ATTACHMENT 28 | schema_class = "AttachmentSchema" 29 | 30 | def __init__( 31 | 32 | self, 33 | *, 34 | message: str = None, 35 | localization: str = None, 36 | files_attach: Sequence[AttachDecorator] = None, 37 | **kwargs, 38 | ): 39 | super(Attachment, self).__init__(**kwargs) 40 | self.message = message 41 | self.files_attach = list(files_attach) if files_attach else [] 42 | if localization: 43 | self._decorators["l10n"] = localization 44 | 45 | @classmethod 46 | def wrap_file(cls, content, filename, mime_type, description=None) -> AttachDecorator: 47 | return AttachDecorator( 48 | ident=str(uuid.uuid4()), 49 | filename=filename, 50 | mime_type=mime_type, 51 | data=AttachDecoratorData( 52 | base64_=bytes_to_b64(content) 53 | ), 54 | description=description 55 | 56 | ) 57 | 58 | 59 | class AttachmentSchema(AgentMessageSchema): 60 | #Schema for Attachment protocol class 61 | 62 | class Meta: 63 | #Metadata 64 | 65 | model_class = Attachment 66 | 67 | message = fields.Str(required=True, description="A message about the file", example="Hey, check out this image") 68 | files_attach = fields.Nested( 69 | AttachDecoratorSchema, required=True, many=True, data_key="files~attach" 70 | ) 71 | 72 | -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/attachment_protocol/v1_0/routes.py: -------------------------------------------------------------------------------- 1 | # Attachment Protocol admin routes 2 | 3 | from marshmallow import fields, Schema 4 | from aiohttp import web, FormData 5 | from aiohttp_apispec import docs, match_info_schema, form_schema, request_schema, response_schema 6 | from aries_cloudagent.connections.models.conn_record import ConnRecord 7 | from aries_cloudagent.messaging.valid import UUIDFour 8 | from aries_cloudagent.storage.error import StorageNotFoundError 9 | from aries_cloudagent.storage.base import BaseStorage 10 | from aries_cloudagent.admin.request_context import AdminRequestContext 11 | 12 | from .messages.attachment import Attachment, AttachmentSchema 13 | 14 | class FileSchema(Schema): 15 | file = fields.Raw( 16 | description="File upload", required=True, type="file", 17 | ) 18 | 19 | class AttachmentMessageSchema(Schema): 20 | message = fields.Str(description="Message about file", example="Here is the file you asked for") 21 | 22 | class ConnIdMatchInfoSchema(Schema): 23 | """Path parameters and validators for request taking connection id.""" 24 | 25 | conn_id = fields.Str( 26 | description="Connection identifier", required=True, example=UUIDFour.EXAMPLE 27 | ) 28 | 29 | @docs(tags=["attachment protocol routes"], summary= "Attach file") 30 | @match_info_schema(ConnIdMatchInfoSchema()) 31 | # @request_schema(AttachmentMessageSchema()) 32 | @form_schema(FileSchema()) 33 | async def send_attachment(request: web.BaseRequest): 34 | """ 35 | Request Handler to send attachment protocol 36 | """ 37 | context: AdminRequestContext = request["context"] 38 | connection_id = request.match_info["conn_id"] 39 | outbound_handler = request["outbound_message_router"] 40 | 41 | session = await context.session() 42 | 43 | # WARNING: don't do that if you plan to receive large files! 44 | # TODO change to handle large files?? 45 | data = await request.post() 46 | 47 | # body = await request.json() 48 | # TODO figure out how to include this in the api. Probable extend the FileSchema 49 | message = "Here is the data you wanted" 50 | 51 | upfile = data['file'] 52 | 53 | 54 | # .filename contains the name of the file in string format. 55 | filename = upfile.filename 56 | print("filename", filename) 57 | content_type = upfile.content_type 58 | 59 | # .file contains the actual file data that needs to be stored somewhere. 60 | file = upfile.file 61 | 62 | content = file.read() 63 | 64 | try: 65 | connection = await ConnRecord.retrieve_by_id(session, connection_id) 66 | except StorageNotFoundError: 67 | raise web.HTTPNotFound() 68 | 69 | if not connection.is_ready: 70 | raise web.HTTPBadRequest() 71 | 72 | attachment = Attachment( 73 | message=message, 74 | files_attach=[Attachment.wrap_file(content, filename, content_type)] 75 | ) 76 | await outbound_handler(attachment, connection_id=connection_id) 77 | 78 | 79 | 80 | return web.json_response({"thread_id": attachment._thread_id}) 81 | 82 | async def register(app: web.Application): 83 | """Register routes.""" 84 | 85 | app.add_routes( 86 | [web.post("/connections/{conn_id}/send-attachment", send_attachment)] 87 | ) -------------------------------------------------------------------------------- /libs/attachment-protocol/attach_protocol/attachment_protocol/v1_0/setup.py: -------------------------------------------------------------------------------- 1 | from aries_cloudagent.config.injection_context import InjectionContext 2 | from aries_cloudagent.core.protocol_registry import ProtocolRegistry 3 | from .message_types import MESSAGE_TYPES 4 | 5 | async def setup( 6 | context: InjectionContext, 7 | protocol_registry: ProtocolRegistry = None 8 | ): 9 | """Setup the attachment protocol plugin.""" 10 | if not protocol_registry: 11 | protocol_registry = await context.inject(ProtocolRegistry) 12 | protocol_registry.register_message_types( 13 | MESSAGE_TYPES 14 | ) -------------------------------------------------------------------------------- /libs/attachment-protocol/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | alice-agent: 4 | image: agent:alice 5 | build: . 6 | environment: 7 | - ADMIN_PORT:${ALICE_ADMIN_PORT} 8 | - HTTP_PORT:${ALICE_HTTP_PORT} 9 | ports: 10 | - ${ALICE_HTTP_PORT}:${ALICE_HTTP_PORT} 11 | - ${ALICE_ADMIN_PORT}:${ALICE_ADMIN_PORT} 12 | entrypoint: /bin/bash 13 | command: [ 14 | "-c", 15 | " 16 | aca-py start \ 17 | --inbound-transport http '0.0.0.0' ${ALICE_HTTP_PORT} \ 18 | --outbound-transport http \ 19 | --endpoint ${ALICE_AGENT_ENDPOINT} \ 20 | --webhook-url ${ALICE_WEBHOOK_URL} \ 21 | --wallet-type 'indy' \ 22 | --seed '${ALICE_WALLET_SEED}' \ 23 | --admin '0.0.0.0' ${ALICE_ADMIN_PORT} \ 24 | --admin-insecure-mode \ 25 | --log-level info \ 26 | --plugin attach_protocol.attachment_protocol \ 27 | --label ${ALICE_AGENT_NAME}", 28 | ] 29 | bob-agent: 30 | image: agent:bob 31 | build: . 32 | environment: 33 | - ADMIN_PORT:${BOB_ADMIN_PORT} 34 | - HTTP_PORT:${BOB_HTTP_PORT} 35 | ports: 36 | - ${BOB_HTTP_PORT}:${BOB_HTTP_PORT} 37 | - ${BOB_ADMIN_PORT}:${BOB_ADMIN_PORT} 38 | entrypoint: /bin/bash 39 | command: [ 40 | "-c", 41 | "aca-py start \ 42 | --inbound-transport http '0.0.0.0' ${BOB_HTTP_PORT} \ 43 | --outbound-transport http \ 44 | --endpoint ${BOB_AGENT_ENDPOINT} \ 45 | --webhook-url ${ALICE_WEBHOOK_URL} \ 46 | --wallet-type 'indy' \ 47 | --seed '${BOB_WALLET_SEED}' \ 48 | --admin '0.0.0.0' ${BOB_ADMIN_PORT} \ 49 | --admin-insecure-mode \ 50 | --log-level info \ 51 | --plugin attach_protocol.attachment_protocol \ 52 | --label ${BOB_AGENT_NAME}", 53 | ] -------------------------------------------------------------------------------- /libs/attachment-protocol/requirements.txt: -------------------------------------------------------------------------------- 1 | aries-cloudagent[indy] 2 | python-dateutil 3 | pyyaml>=5.4 # not directly required, pinned by Snyk to avoid a vulnerability -------------------------------------------------------------------------------- /libs/attachment-protocol/setup.py: -------------------------------------------------------------------------------- 1 | # Attachment protocol 2 | 3 | from setuptools import setup, find_packages 4 | 5 | def parse_requirements(filename): 6 | """Load requirements from a pip requirements file.""" 7 | lineiter = (line.strip() for line in open(filename)) 8 | return [line for line in lineiter if line and not line.startswith("#")] 9 | 10 | if __name__ == '__main__': 11 | # with open('README.md', 'r') as fh: 12 | # LONG_DESCRIPTION = fh.read() 13 | 14 | setup( 15 | name='attachment-protocol', 16 | version='0.1.0', 17 | author='Vineeth Rajesh , ', 18 | description='Aries Cloud Agent - Python Attachment Protocol Plugin Example', 19 | # long_description=LONG_DESCRIPTION, 20 | # long_description_content_type='text/markdown', 21 | # url='https://github.com/sovrin-foundation/aca-plugin-toolbox', 22 | license='Apache 2.0', 23 | packages=find_packages(), 24 | install_requires=parse_requirements('requirements.txt'), 25 | python_requires='>=3.6', 26 | classifiers=[ 27 | 'Programming Language :: Python :: 3', 28 | 'License :: OSI Approved :: Apache Software License', 29 | 'Operating System :: OS Independent' 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /projects/aries-fl/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/.DS_Store -------------------------------------------------------------------------------- /projects/aries-fl/Dockerfile.hospitalcontroller: -------------------------------------------------------------------------------- 1 | FROM continuumio/miniconda3 2 | 3 | ENV WORKSPACE /workspace 4 | 5 | ARG jupyter_port 6 | ENV env_jupyter_port=jupyter_port 7 | 8 | # Setup workspace environment 9 | RUN apt-get update && apt-get install -y gcc 10 | # RUN conda install -c conda-forge jupyterlab==3.0.11 11 | RUN conda install -c conda-forge jupyterlab==3.0.11 12 | 13 | 14 | ADD projects/aries-fl/requirements.txt . 15 | 16 | # 17 | # . 18 | RUN pip install -r requirements.txt 19 | 20 | #RUN pip install aiohttp 21 | #RUN export PYTHONPATH="$PYTHONPATH:/aries_basic_controller" 22 | #ENV PYTHONPATH "${PYTHONPATH}:/aries_basic_controller" 23 | #RUN export JUPYTER_PATH="$JUPYTER_PATH:/aries_basic_controller" 24 | # Create jupyter notebook workspace 25 | #RUN mkdir $WORKSPACE 26 | WORKDIR $WORKSPACE 27 | 28 | 29 | # Make the image start the jupyer notebook 30 | COPY scripts/jupyter-entrypoint.sh /entrypoint.sh 31 | RUN chmod +x /entrypoint.sh 32 | 33 | 34 | 35 | 36 | 37 | # ENTRYPOINT ["/entrypoint.sh", "$env_jupyter_port"] 38 | ENTRYPOINT ["/entrypoint.sh", "8888"] -------------------------------------------------------------------------------- /projects/aries-fl/Dockerfile.ppmlcontroller: -------------------------------------------------------------------------------- 1 | FROM continuumio/miniconda3 2 | 3 | ENV WORKSPACE /workspace 4 | 5 | ARG jupyter_port 6 | ENV env_jupyter_port=jupyter_port 7 | 8 | # Setup workspace environment 9 | RUN apt-get update && apt-get install -y gcc 10 | # RUN conda install -c conda-forge jupyterlab==3.0.11 11 | RUN conda install -c conda-forge jupyterlab==3.0.11 12 | 13 | 14 | ADD projects/aries-fl/requirements.txt . 15 | 16 | # 17 | # . 18 | RUN pip install -r requirements.txt 19 | 20 | #RUN pip install aiohttp 21 | #RUN export PYTHONPATH="$PYTHONPATH:/aries_basic_controller" 22 | #ENV PYTHONPATH "${PYTHONPATH}:/aries_basic_controller" 23 | #RUN export JUPYTER_PATH="$JUPYTER_PATH:/aries_basic_controller" 24 | # Create jupyter notebook workspace 25 | #RUN mkdir $WORKSPACE 26 | WORKDIR $WORKSPACE 27 | 28 | 29 | # Make the image start the jupyer notebook 30 | COPY scripts/jupyter-entrypoint.sh /entrypoint.sh 31 | RUN chmod +x /entrypoint.sh 32 | 33 | 34 | 35 | 36 | 37 | # ENTRYPOINT ["/entrypoint.sh", "$env_jupyter_port"] 38 | ENTRYPOINT ["/entrypoint.sh", "8888"] -------------------------------------------------------------------------------- /projects/aries-fl/README.md: -------------------------------------------------------------------------------- 1 | # Federated Learning over DIDComm 2 | 3 | 4 | 5 | # Guide 6 | 7 | ![Full Aries Scenario](figures/initialidea.png) 8 | 9 | 10 | 11 | # Created by 12 | 13 | Will Abramson (wip-abramson) 14 | Adam James Hall (H4LL) 15 | Pavlos Papadopoulos (pavlos-p) 16 | 17 | ## Publications 18 | Papadopoulos, P., Abramson, W., Hall, A. J., Pitropakis, N., & Buchanan, W. J. (2021). Privacy and Trust Redefined in Federated Machine Learning. Machine Learning and Knowledge Extraction, 3(2), 333-356. ([link](https://www.mdpi.com/2504-4990/3/2/17)) 19 | 20 | Abramson, W., Hall, A. J., Papadopoulos, P., Pitropakis, N., & Buchanan, W. J. (2020, September). A Distributed Trust Framework for Privacy-Preserving Machine Learning. In International Conference on Trust and Privacy in Digital Business (pp. 205-220). Springer, Cham. ([link](https://link.springer.com/chapter/10.1007/978-3-030-58986-8_14) and [link](https://arxiv.org/abs/2006.02456)) 21 | 22 | You can cite this work using: 23 | 24 | @article{papadopoulos2021privacy, 25 | title={Privacy and Trust Redefined in Federated Machine Learning}, 26 | author={Papadopoulos, Pavlos and Abramson, Will and Hall, Adam J and Pitropakis, Nikolaos and Buchanan, William J}, 27 | journal={Machine Learning and Knowledge Extraction}, 28 | volume={3}, 29 | number={2}, 30 | pages={333--356}, 31 | year={2021}, 32 | publisher={Multidisciplinary Digital Publishing Institute} 33 | } 34 | 35 | @article{abramson2020distributed, 36 | title={A Distributed Trust Framework for Privacy-Preserving Machine Learning}, 37 | ISBN={9783030589868}, 38 | ISSN={1611-3349}, 39 | url={http://dx.doi.org/10.1007/978-3-030-58986-8_14}, 40 | DOI={10.1007/978-3-030-58986-8_14}, 41 | journal={Lecture Notes in Computer Science}, 42 | publisher={Springer International Publishing}, 43 | author={Abramson, Will and Hall, Adam James and Papadopoulos, Pavlos and Pitropakis, Nikolaos and Buchanan, William J.}, 44 | year={2020}, 45 | pages={205–220} 46 | } 47 | 48 | -------------------------------------------------------------------------------- /projects/aries-fl/figures/Trust_to_Hospital.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/figures/Trust_to_Hospital.png -------------------------------------------------------------------------------- /projects/aries-fl/figures/VanillaFL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/figures/VanillaFL.png -------------------------------------------------------------------------------- /projects/aries-fl/figures/coordinator_and_hospital.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/figures/coordinator_and_hospital.png -------------------------------------------------------------------------------- /projects/aries-fl/figures/federated_learning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/figures/federated_learning.png -------------------------------------------------------------------------------- /projects/aries-fl/figures/initialidea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/figures/initialidea.png -------------------------------------------------------------------------------- /projects/aries-fl/figures/regulator_to_coordinator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/figures/regulator_to_coordinator.png -------------------------------------------------------------------------------- /projects/aries-fl/figures/trusted_connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/figures/trusted_connection.png -------------------------------------------------------------------------------- /projects/aries-fl/hospital/__init__.py: -------------------------------------------------------------------------------- 1 | from hospital.hospital import Hospital -------------------------------------------------------------------------------- /projects/aries-fl/notebooks/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/notebooks/.DS_Store -------------------------------------------------------------------------------- /projects/aries-fl/notebooks/Test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/aries-fl/notebooks/Test/.DS_Store -------------------------------------------------------------------------------- /projects/aries-fl/notebooks/nhs_trust/Part 1 - Aries Federated Learning.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Aries Federated Learning Playground\n", 8 | "\n", 9 | "In this set of notebooks we are combining federated learning with the authenticated communication and credential capabilities of Hyperledger Aries agents.\n", 10 | "\n", 11 | "It builds on ideas first set out in this paper: [A Distributed Trust Framework for Privacy-Preserving Machine Learning](https://www.researchgate.net/publication/341932337_A_Distributed_Trust_Framework_for_Privacy-Preserving_Machine_Learning)" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## Trust Framework\n", 19 | "\n", 20 | "The idea is that we can use Verifiable Credentials to authenticate different participants in a federated learning workflow. This set of notebooks demonstrates this within a health setting where a researcher wishes to train a model on data held by hospitals. Two authorities are considered, an NHS Trust who is the healthcare authority for a set of Hospitals and a Health Research Authority responsible for accrediting researchers to perform specific research.\n", 21 | "\n", 22 | "For training to be initiated the following rules must be met:\n", 23 | "\n", 24 | "* A Researcher will only send their model to a Hospital if they can prove they are indeed a Hospital be presenting a credential issued from the NHS Trust.\n", 25 | "* A Hospital will only train a model they receive if the research can prove they have had their research accredited by the Health Research Authority.\n", 26 | "\n" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [] 35 | } 36 | ], 37 | "metadata": { 38 | "kernelspec": { 39 | "display_name": "Python 3", 40 | "language": "python", 41 | "name": "python3" 42 | }, 43 | "language_info": { 44 | "codemirror_mode": { 45 | "name": "ipython", 46 | "version": 3 47 | }, 48 | "file_extension": ".py", 49 | "mimetype": "text/x-python", 50 | "name": "python", 51 | "nbconvert_exporter": "python", 52 | "pygments_lexer": "ipython3", 53 | "version": "3.7.6" 54 | } 55 | }, 56 | "nbformat": 4, 57 | "nbformat_minor": 4 58 | } 59 | -------------------------------------------------------------------------------- /projects/aries-fl/requirements.hospital.txt: -------------------------------------------------------------------------------- 1 | numpy==1.17.3 2 | pandas==0.25.2 3 | matplotlib==3.1.1 4 | seaborn==0.9.0 5 | https://download.pytorch.org/whl/cpu/torch-1.7.0%2Bcpu-cp36-cp36m-linux_x86_64.whl 6 | sklearn 7 | opacus -------------------------------------------------------------------------------- /projects/aries-fl/requirements.txt: -------------------------------------------------------------------------------- 1 | aries-basic-controller 2 | numpy==1.17.3 3 | pandas==0.25.2 4 | matplotlib==3.1.1 5 | seaborn==0.9.0 6 | torch 7 | sklearn 8 | opacus 9 | -------------------------------------------------------------------------------- /projects/doctors-in-training/README.md: -------------------------------------------------------------------------------- 1 | # Doctors in Training 2 | 3 | ### A real life use case for verifiable credentials developed at a recent INTEROpen Staff Access hackathon 4 | 5 | The use case follows the following sequence diagram 6 | 7 | ![sequencediagram](./sequence.jpg) 8 | 9 | ### Running the example 10 | 11 | With docker installed, run the example using `./manage start` 12 | 13 | To go the first notebook [ID Verification](http://127.0.0.1:8888) 14 | 15 | The notebooks requires a token that can be found in the logs. The token for the ID Verification Service can be fetched using the following command. 16 | 17 | * `docker logs doctors-in-training_id-verifier-agent_1` 18 | 19 | 20 | You can also fetch all notebook urls with tokens by running `./get_URLS.sh` 21 | -------------------------------------------------------------------------------- /projects/doctors-in-training/notebooks/gmc/issuer_agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/doctors-in-training/notebooks/gmc/issuer_agent_invite_QRcode.png -------------------------------------------------------------------------------- /projects/doctors-in-training/notebooks/hee/issuer_agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/doctors-in-training/notebooks/hee/issuer_agent_invite_QRcode.png -------------------------------------------------------------------------------- /projects/doctors-in-training/notebooks/hee/verifier_agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/doctors-in-training/notebooks/hee/verifier_agent_invite_QRcode.png -------------------------------------------------------------------------------- /projects/doctors-in-training/notebooks/id-verifier/1. DiT Sequence Diagram B.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/doctors-in-training/notebooks/id-verifier/1. DiT Sequence Diagram B.pdf -------------------------------------------------------------------------------- /projects/doctors-in-training/notebooks/id-verifier/id-verifier-agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/doctors-in-training/notebooks/id-verifier/id-verifier-agent_invite_QRcode.png -------------------------------------------------------------------------------- /projects/doctors-in-training/notebooks/lead-employer/issuer_agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/doctors-in-training/notebooks/lead-employer/issuer_agent_invite_QRcode.png -------------------------------------------------------------------------------- /projects/doctors-in-training/notebooks/medical-school/issuer_agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/doctors-in-training/notebooks/medical-school/issuer_agent_invite_QRcode.png -------------------------------------------------------------------------------- /projects/doctors-in-training/notebooks/medical-school/medical-school-agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/doctors-in-training/notebooks/medical-school/medical-school-agent_invite_QRcode.png -------------------------------------------------------------------------------- /projects/doctors-in-training/sequence.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/doctors-in-training/sequence.jpg -------------------------------------------------------------------------------- /projects/opus/README.md: -------------------------------------------------------------------------------- 1 | # Opus 2 | 3 | Opus is a SSI-driven third party data onboarding project. 4 | The project is in early development. 5 | 6 | drawing 7 | 8 | ## Pre-requisites 9 | 10 | 1. Docker 11 | 12 | ## Demo Environment 13 | 14 | - Opus Business Logic Notebook:8889 15 | - Opus Agent:8051 16 | - Alice Notebook:8888 17 | - Alice Agent:8021 18 | - Ledger Browser:9000 19 | 20 | ## How to run 21 | 22 | Run `bash ./manage up` from this directory 23 | 24 | In the terminal output you'll find the tokens for your notebooks; 25 | 26 | drawing 27 | 28 | Begin the tutorials from the Opus Notebook. Notebooks mount their respective directories in './demo'. Method 2 has been completed. Enjoy! 29 | -------------------------------------------------------------------------------- /projects/opus/demo/alice/images/opus_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/demo/alice/images/opus_user.png -------------------------------------------------------------------------------- /projects/opus/demo/opus/images/credential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/demo/opus/images/credential.png -------------------------------------------------------------------------------- /projects/opus/demo/opus/images/opus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/demo/opus/images/opus.png -------------------------------------------------------------------------------- /projects/opus/demo/opus/images/opus_1A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/demo/opus/images/opus_1A.png -------------------------------------------------------------------------------- /projects/opus/demo/opus/images/opus_1B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/demo/opus/images/opus_1B.png -------------------------------------------------------------------------------- /projects/opus/demo/opus/images/opus_2B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/demo/opus/images/opus_2B.png -------------------------------------------------------------------------------- /projects/opus/demo/opus/images/scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/demo/opus/images/scheme.png -------------------------------------------------------------------------------- /projects/opus/demo/setup/create_connection.py: -------------------------------------------------------------------------------- 1 | import os 2 | import asyncio 3 | import time 4 | 5 | from aries_basic_controller.aries_controller import AriesAgentController 6 | 7 | from dotenv import load_dotenv 8 | load_dotenv() 9 | 10 | ALICE_ADMIN_URL = os.getenv('ALICE_ADMIN_URL') 11 | ALICE_WEBHOOK_PORT = os.getenv('ALICE_WEBHOOK_PORT') 12 | ALICE_WEBHOOK_HOST = os.getenv('ALICE_WEBHOOK_HOST') 13 | ALICE_WEBHOOK_BASE = os.getenv('ALICE_WEBHOOK_BASE') 14 | 15 | BOB_ADMIN_URL = os.getenv('BOB_ADMIN_URL') 16 | BOB_WEBHOOK_PORT = os.getenv('BOB_WEBHOOK_PORT') 17 | BOB_WEBHOOK_HOST = os.getenv('BOB_WEBHOOK_HOST') 18 | BOB_WEBHOOK_BASE = os.getenv('BOB_WEBHOOK_BASE') 19 | 20 | 21 | 22 | 23 | 24 | async def start_agent(): 25 | 26 | time.sleep(6) 27 | 28 | bob_agent_controller = AriesAgentController(webhook_host=BOB_WEBHOOK_HOST, webhook_port=BOB_WEBHOOK_PORT, 29 | webhook_base=BOB_WEBHOOK_BASE, admin_url=BOB_ADMIN_URL, connections=True) 30 | 31 | 32 | 33 | alice_agent_controller = AriesAgentController(webhook_host=ALICE_WEBHOOK_HOST, webhook_port=ALICE_WEBHOOK_PORT, 34 | webhook_base=ALICE_WEBHOOK_BASE, admin_url=ALICE_ADMIN_URL, connections=True) 35 | 36 | 37 | await alice_agent_controller.listen_webhooks() 38 | 39 | await bob_agent_controller.listen_webhooks() 40 | 41 | 42 | bob_agent_controller.register_listeners([], defaults=True) 43 | alice_agent_controller.register_listeners([], defaults=True) 44 | 45 | invite = await bob_agent_controller.connections.create_invitation() 46 | print("Invite", invite) 47 | 48 | bob_connection_id = invite["connection_id"] 49 | 50 | response = await alice_agent_controller.connections.accept_connection(invite["invitation"]) 51 | print(response) 52 | 53 | 54 | print("Alice ID", response["connection_id"]) 55 | alice_id = response["connection_id"] 56 | print("Invite Accepted") 57 | print(response) 58 | 59 | 60 | connection = await bob_agent_controller.connections.accept_request(bob_connection_id) 61 | print("ACCEPT REQUEST") 62 | print(connection) 63 | 64 | connection = await bob_agent_controller.connections.get_connection(bob_connection_id) 65 | print("BOB AGENT CONNECTION") 66 | print(connection) 67 | 68 | while connection["state"] != "active": 69 | trust_ping = await bob_agent_controller.messaging.trust_ping(bob_connection_id, "hello") 70 | print("TUST PING TO ACTIVATE CONNECTION - BOB -> RESEARCH") 71 | print(trust_ping) 72 | time.sleep(5) 73 | connection = await bob_agent_controller.connections.get_connection(bob_connection_id) 74 | 75 | trust_ping = await alice_agent_controller.messaging.trust_ping(alice_id,"hello") 76 | print("TUST PING TO ACTIVATE CONNECTION - RESEARCH -> BOB") 77 | print(trust_ping) 78 | 79 | print("ALICE ID {} BOB ID {}".format(alice_id, bob_connection_id)) 80 | 81 | connection = await bob_agent_controller.connections.get_connection(bob_connection_id) 82 | print("BOB AGENT CONNECTION") 83 | print(connection) 84 | 85 | connection = await alice_agent_controller.connections.get_connection(alice_id) 86 | print("RESEARCH AGENT CONNECTION") 87 | print(connection) 88 | 89 | print("SUCCESS") 90 | time.sleep(2) 91 | await bob_agent_controller.terminate() 92 | await alice_agent_controller.terminate() 93 | 94 | 95 | if __name__ == "__main__": 96 | # time.sleep(60) 97 | try: 98 | asyncio.get_event_loop().run_until_complete(start_agent()) 99 | except KeyboardInterrupt: 100 | os._exit(1) 101 | 102 | -------------------------------------------------------------------------------- /projects/opus/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | bob-agent: 4 | image: bcgovimages/aries-cloudagent:py36-1.14-1_0.5.1 5 | ports: 6 | - ${BOB_HTTP_PORT}:${BOB_HTTP_PORT} 7 | - ${BOB_ADMIN_PORT}:${BOB_ADMIN_PORT} 8 | depends_on: 9 | - ledger-nodes 10 | networks: 11 | - indy_demo 12 | entrypoint: /bin/bash 13 | command: [ 14 | "-c", 15 | "echo Waiting for ledger to be ready...; 16 | sleep 60; 17 | curl -d '{\"seed\":\"${BOB_WALLET_SEED}\", \"role\":\"TRUST_ANCHOR\", \"alias\":\"${BOB_AGENT_NAME}\"}' -X POST ${LEDGER_URL}/register; \ 18 | sleep 2; \ 19 | aca-py start \ 20 | --inbound-transport http '0.0.0.0' ${BOB_HTTP_PORT} \ 21 | --outbound-transport http \ 22 | --endpoint ${BOB_AGENT_ENDPOINT} \ 23 | --webhook-url ${BOB_WEBHOOK_URL} \ 24 | --wallet-type 'indy' \ 25 | --seed '${BOB_WALLET_SEED}' \ 26 | --admin '0.0.0.0' ${BOB_ADMIN_PORT} \ 27 | --admin-insecure-mode \ 28 | --log-level info \ 29 | --genesis-url '${LEDGER_URL}/genesis' 30 | --label ${BOB_AGENT_NAME}", 31 | ] 32 | alice-agent: 33 | image: bcgovimages/aries-cloudagent:py36-1.14-1_0.5.1 34 | ports: 35 | - ${ALICE_HTTP_PORT}:${ALICE_HTTP_PORT} 36 | - ${ALICE_ADMIN_PORT}:${ALICE_ADMIN_PORT} 37 | depends_on: 38 | - ledger-nodes 39 | networks: 40 | - indy_demo 41 | # volumes: 42 | # - alice-wallet:/indy/home/.indy_client/default 43 | entrypoint: /bin/bash 44 | command: [ 45 | "-c", 46 | "echo Waiting for ledger to be ready...; 47 | sleep 60; 48 | curl -d '{\"seed\":\"${ALICE_WALLET_SEED}\", \"role\":\"TRUST_ANCHOR\", \"alias\":\"${ALICE_AGENT_NAME}\"}' -X POST ${LEDGER_URL}/register; \ 49 | sleep 2; \ 50 | aca-py start \ 51 | --inbound-transport http '0.0.0.0' ${ALICE_HTTP_PORT} \ 52 | --outbound-transport http \ 53 | --endpoint ${ALICE_AGENT_ENDPOINT} \ 54 | --webhook-url ${ALICE_WEBHOOK_URL} \ 55 | --wallet-type 'indy' \ 56 | --seed '${ALICE_WALLET_SEED}' \ 57 | --admin '0.0.0.0' ${ALICE_ADMIN_PORT} \ 58 | --admin-insecure-mode \ 59 | --log-level info \ 60 | --genesis-url '${LEDGER_URL}/genesis' 61 | --label ${ALICE_AGENT_NAME}", 62 | ] 63 | setup: 64 | build: 65 | context: ../../libs/aries-basic-controller 66 | dockerfile: Dockerfile.setup 67 | networks: 68 | - indy_demo 69 | depends_on: 70 | - alice-agent 71 | - bob-agent 72 | # ports: 73 | # - ${ALICE_WEBHOOK_PORT}:${ALICE_WEBHOOK_PORT} 74 | # - ${BOB_WEBHOOK_PORT}:${BOB_WEBHOOK_PORT} 75 | environment: 76 | - ALICE_ADMIN_URL=http://alice-agent:${ALICE_ADMIN_PORT} 77 | - ALICE_WEBHOOK_PORT=${ALICE_WEBHOOK_PORT} 78 | - ALICE_WEBHOOK_HOST=0.0.0.0 79 | - BOB_ADMIN_URL=http://bob-agent:${BOB_ADMIN_PORT} 80 | - BOB_WEBHOOK_PORT=${BOB_WEBHOOK_PORT} 81 | - BOB_WEBHOOK_HOST=0.0.0.0 82 | entrypoint: /bin/bash 83 | command: 84 | [ 85 | "-c", 86 | "sleep 80; 87 | python ./create_connection.py" 88 | ] 89 | alice-notebook: 90 | build: 91 | context: ../../libs/aries-basic-controller 92 | args: 93 | - jupyter_port=${ALICE_JUPYTER_PORT} 94 | depends_on: 95 | - alice-agent 96 | networks: 97 | - indy_demo 98 | volumes: 99 | - ./demo/alice:/workspace 100 | ports: 101 | - "8888:8888" 102 | - ${ALICE_WEBHOOK_PORT}:${ALICE_WEBHOOK_PORT} 103 | bob-notebook: 104 | build: 105 | context: ../../libs/aries-basic-controller 106 | args: 107 | - jupyter_port=${BOB_JUPYTER_PORT} 108 | depends_on: 109 | - bob-agent 110 | networks: 111 | - indy_demo 112 | volumes: 113 | - ${PWD}/demo/opus:/workspace 114 | ports: 115 | - "8889:8888" 116 | - "5000:5000" 117 | - ${BOB_WEBHOOK_PORT}:${BOB_WEBHOOK_PORT} 118 | ledger-browser: 119 | build: 120 | context: https://github.com/bcgov/von-network.git 121 | dockerfile: Dockerfile 122 | command: "bash -c 'sleep 10; ./scripts/start_webserver.sh;'" 123 | environment: 124 | - DOCKERHOST=${DOCKERHOST} 125 | - MAX_FETCH=50000 126 | - RESYNC_TIME=120 127 | - REGISTER_NEW_DIDS=True 128 | - LEDGER_INSTANCE_NAME=localhost 129 | ports: 130 | - ${WEB_SERVER_HOST_PORT:-9000}:8000 131 | volumes: 132 | - webserver-cli:/home/indy/.indy-cli 133 | - webserver-ledger:/home/indy/ledger 134 | networks: 135 | - indy_demo 136 | 137 | ledger-nodes: 138 | build: 139 | context: https://github.com/bcgov/von-network.git 140 | dockerfile: Dockerfile 141 | command: "bash -c './scripts/start_nodes.sh'" 142 | ports: 143 | - 9701:9701 144 | - 9702:9702 145 | - 9703:9703 146 | - 9704:9704 147 | - 9705:9705 148 | - 9706:9706 149 | - 9707:9707 150 | - 9708:9708 151 | environment: 152 | - DOCKERHOST=${DOCKERHOST} 153 | volumes: 154 | - nodes-data:/home/indy/ledger 155 | networks: 156 | - indy_demo 157 | 158 | networks: 159 | indy_demo: 160 | volumes: 161 | webserver-cli: 162 | webserver-ledger: 163 | nodes-data: 164 | -------------------------------------------------------------------------------- /projects/opus/images/instructions1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/images/instructions1.png -------------------------------------------------------------------------------- /projects/opus/images/opus_1A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/images/opus_1A.png -------------------------------------------------------------------------------- /projects/opus/images/opus_1B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/images/opus_1B.png -------------------------------------------------------------------------------- /projects/opus/images/opus_2B.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/opus/images/opus_2B.PNG -------------------------------------------------------------------------------- /projects/pryvote/README.md: -------------------------------------------------------------------------------- 1 | # PryVote 2 | 3 | PryVote is a SSI-driven voting project 4 | which builds on the `aries-basic-controller`, 5 | developed in `PyDentity`, 6 | to implement logic for holding voting sessions 7 | and casting votes. 8 | We use secure multi-party computation, 9 | differential privacy 10 | and functional encryption 11 | to secure votes 12 | and identities. 13 | 14 | The project is in early development. 15 | 16 | ## Examples 17 | 18 | #### [Secure Multi-Party Voting](./demo/1_secure_multi_party_voting.ipynb) 19 | 20 | POC of secure multi-party voting. 21 | Each vote is encrypted and split 22 | between multiple vote counters. 23 | At the end of voting, 24 | the vote counters combine 25 | their encrypted shares 26 | to reveal the aggregate result. 27 | 28 | **No credential verification/SSI in place at the moment.** 29 | 30 | #### [Shamir Secret Sharing](./demo/2_shamir_secret_sharing_voting.ipynb) 31 | 32 | The Shamir Secret Sharing protocol 33 | is similar to SMPV, 34 | however only a subset of vote counters 35 | are required for the aggregate result 36 | to be decrypted. 37 | Therefore, 38 | this protocol is somewhat robust 39 | to malicious 40 | vote counters. 41 | 42 | **No credential verification/SSI in place at the moment.** 43 | -------------------------------------------------------------------------------- /projects/pryvote/images/smpv-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/pryvote/images/smpv-1.png -------------------------------------------------------------------------------- /projects/pryvote/images/smpv-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/pryvote/images/smpv-2.png -------------------------------------------------------------------------------- /projects/single-agent/README.md: -------------------------------------------------------------------------------- 1 | # IIW OpenMined ACA-Py, aries-basic-controller & Juypter Notebook demo 2 | 3 | 4 | * You need [Docker](https://docs.docker.com/compose/install/) 5 | * You need [Source 2 Image](https://github.com/openshift/source-to-image) 6 | 7 | ### Config 8 | 9 | In the manage file you can configure the parameters of your agent. You may want to change your agents name and api_key. Although this is only for fun so I don't suppose it really matters. 10 | 11 | You can also edit your agents configuration flags in the startup.sh file. 12 | 13 | 14 | ### Rum 15 | 16 | To start: `./manage start`. You might need to make the manage file executable. 17 | 18 | Fetch the jupyter notebook token by running `docker logs iiw_iiw-notebook_1` 19 | 20 | Navigate to the notebook - [https://localhost:8888](https://localhost:8888) 21 | 22 | To stop and keep volumes: `./manage stop` 23 | 24 | To stop and tear down volumes: `./manage down` -------------------------------------------------------------------------------- /projects/single-agent/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | ngrok-agent: 4 | image: wernight/ngrok 5 | command: ngrok http agent:${AGENT_HTTP_PORT} --log stdout 6 | networks: 7 | - indy_demo 8 | agent: 9 | build: 10 | context: ../../ 11 | dockerfile: dockerfiles/agents/Dockerfile.ngrok 12 | environment: 13 | - NGROK_NAME=ngrok-agent 14 | - ADMIN_URL=http://agent:${AGENT_ADMIN_PORT} 15 | - AGENT_NAME=${AGENT_NAME} 16 | - ADMIN_PORT=${AGENT_ADMIN_PORT} 17 | - HTTP_PORT=${AGENT_HTTP_PORT} 18 | - AGENT_ENDPOINT=${AGENT_ENDPOINT} 19 | - ACAPY_WEBHOOK_URL=${AGENT_WEBHOOK_URL} 20 | - WALLET_NAME=${AGENT_WALLET_NAME} 21 | - WALLET_TYPE=${WALLET_TYPE} 22 | - WALLET_KEY=${AGENT_WALLET_KEY} 23 | - ACAPY_ADMIN_INSECURE_MODE=true 24 | - GENESIS_URL=${GENESIS_URL} 25 | ports: 26 | - ${AGENT_HTTP_PORT}:${AGENT_HTTP_PORT} 27 | - ${AGENT_ADMIN_PORT}:${AGENT_ADMIN_PORT} 28 | networks: 29 | - indy_demo 30 | volumes: 31 | - agent:/home/indy/.indy_client/wallet 32 | controller-notebook: 33 | build: 34 | context: ../../ 35 | dockerfile: dockerfiles/controllers/Dockerfile.attachmentcontroller 36 | args: 37 | - jupyter_port=${JUPYTER_PORT} 38 | depends_on: 39 | - agent 40 | networks: 41 | - indy_demo 42 | volumes: 43 | - ./notebooks:/workspace 44 | ports: 45 | - "8888:8888" 46 | - ${AGENT_WEBHOOK_PORT}:${AGENT_WEBHOOK_PORT} 47 | volumes: 48 | agent: 49 | networks: 50 | indy_demo: 51 | -------------------------------------------------------------------------------- /projects/single-agent/notebooks/iiw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/single-agent/notebooks/iiw.png -------------------------------------------------------------------------------- /projects/single-agent/notebooks/issuer_agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/projects/single-agent/notebooks/issuer_agent_invite_QRcode.png -------------------------------------------------------------------------------- /scripts/clean_notebook_output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # script to recursively go through all notebooks and clean the output cells 3 | find .. -name "*.ipynb" -exec jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace {} \; -------------------------------------------------------------------------------- /scripts/get_URLS.sh: -------------------------------------------------------------------------------- 1 | #script to get notebook URLs 2 | for i in `docker ps --format "table {{.Names}}" | grep notebook`; do port=$(docker port $i 8888/tcp | cut -d":" -f2);token=$(docker logs $i 2>&1 | grep "127.0.0.1:8888/" | cut -d"=" -f2 | head -1); echo "$i - http://localhost:$port/?token=$token"; done 3 | for i in `docker ps --format "table {{.Names}}" | grep business-logic`; do port=$(docker port $i 8888/tcp | cut -d":" -f2);token=$(docker logs $i 2>&1 | grep "127.0.0.1:8888/" | cut -d"=" -f2 | head -1); echo "$i - http://localhost:$port/?token=$token"; done -------------------------------------------------------------------------------- /scripts/jupyter-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Use the passed WORKSPACE DIRECTORY 4 | # Otherwise use /workspace 5 | if [ -z "${WORKSPACE_DIR}" ]; then 6 | WORKSPACE="/workspace" 7 | else 8 | WORKSPACE="${WORKSPACE_DIR}" 9 | fi 10 | 11 | cd $WORKSPACE 12 | echo "HELLO THIS IS PORT $1" 13 | jupyter lab --ip=0.0.0.0 --port="$1" --allow-root 14 | -------------------------------------------------------------------------------- /scripts/ngrok-wait.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NGROK_NAME=${NGROK_NAME:-ngrok} 4 | 5 | echo "ngrok end point [$NGROK_NAME]" 6 | 7 | AGENT_ENDPOINT=null 8 | while [ -z "$AGENT_ENDPOINT" ] || [ "$AGENT_ENDPOINT" = "null" ] 9 | do 10 | echo "Fetching end point from ngrok service" 11 | AGENT_ENDPOINT=$(curl --silent $NGROK_NAME:4040/api/tunnels | ./jq -r '.tunnels[0].public_url') 12 | echo "ngrok end point [$AGENT_ENDPOINT]" 13 | if [ -z "$AGENT_ENDPOINT" ] || [ "$AGENT_ENDPOINT" = "null" ]; then 14 | echo "ngrok not ready, sleeping 5 seconds...." 15 | sleep 5 16 | fi 17 | done 18 | 19 | echo "fetched end point [$AGENT_ENDPOINT]" 20 | 21 | export AGENT_ENDPOINT=$AGENT_ENDPOINT 22 | exec "$@" -------------------------------------------------------------------------------- /scripts/parse_yml_env_variables.py: -------------------------------------------------------------------------------- 1 | # To run this: 2 | # export DB_PASS=very_secret_and_complex 3 | # python use_env_variables_in_config_example.py -c /path/to/yaml 4 | # do stuff with conf, e.g. access the database password like this: conf['database']['DB_PASS'] 5 | # YAML FILE 6 | # genesis-url: !ENV ${GENESIS_URL} 7 | import argparse 8 | 9 | import yaml 10 | from yml_env_variables import parse_config 11 | 12 | if __name__ == '__main__': 13 | parser = argparse.ArgumentParser(description='YAML environment variables substitution') 14 | parser.add_argument( 15 | "-c", "--conf", action="store", dest="conf_file", 16 | help="Path to YAML config file" 17 | ) 18 | args = parser.parse_args() 19 | print(args) 20 | conf = parse_config(path=args.conf_file) 21 | print(conf) 22 | with open(r'/tmp/agent_conf.yml', 'w') as file: 23 | documents = yaml.dump(conf, file) -------------------------------------------------------------------------------- /scripts/startup-new.sh: -------------------------------------------------------------------------------- 1 | aca-py start \ 2 | -it http '0.0.0.0' "$HTTP_PORT" \ 3 | -e "$AGENT_ENDPOINT" "${AGENT_ENDPOINT/http/ws}" \ 4 | "$@" -------------------------------------------------------------------------------- /scripts/startup.sh: -------------------------------------------------------------------------------- 1 | if [ "$MULTITENANT_TUTORIAL" == "MULTITENANT_TUTORIAL" ]; then 2 | echo $MULTITENANT_TUTORIAL 3 | python3 ./scripts/parse_yml_env_variables.py -c ./configuration/aries-args-multitenant.yaml;aca-py start --arg-file /tmp/agent_conf.yml 4 | else 5 | echo "Advanced Args used" 6 | python3 ./scripts/parse_yml_env_variables.py -c ./configuration/aries-args-advanced.yaml;aca-py start --arg-file /tmp/agent_conf.yml 7 | fi -------------------------------------------------------------------------------- /scripts/yml_env_variables.py: -------------------------------------------------------------------------------- 1 | #https://gist.github.com/mkaranasou/ba83e25c835a8f7629e34dd7ede01931#file-python_yaml_environment_variables-py 2 | import os 3 | import re 4 | import yaml 5 | 6 | 7 | def parse_config(path=None, data=None, tag='!ENV'): 8 | """ 9 | Load a yaml configuration file and resolve any environment variables 10 | The environment variables must have !ENV before them and be in this format 11 | to be parsed: ${VAR_NAME}. 12 | E.g.: 13 | 14 | database: 15 | host: !ENV ${HOST} 16 | port: !ENV ${PORT} 17 | app: 18 | log_path: !ENV '/var/${LOG_PATH}' 19 | something_else: !ENV '${AWESOME_ENV_VAR}/var/${A_SECOND_AWESOME_VAR}' 20 | 21 | :param str path: the path to the yaml file 22 | :param str data: the yaml data itself as a stream 23 | :param str tag: the tag to look for 24 | :return: the dict configuration 25 | :rtype: dict[str, T] 26 | """ 27 | # pattern for global vars: look for ${word} 28 | pattern = re.compile('.*?\${(\w+)}.*?') 29 | loader = yaml.SafeLoader 30 | 31 | # the tag will be used to mark where to start searching for the pattern 32 | # e.g. somekey: !ENV somestring${MYENVVAR}blah blah blah 33 | loader.add_implicit_resolver(tag, pattern, None) 34 | 35 | def constructor_env_variables(loader, node): 36 | """ 37 | Extracts the environment variable from the node's value 38 | :param yaml.Loader loader: the yaml loader 39 | :param node: the current node in the yaml 40 | :return: the parsed string that contains the value of the environment 41 | variable 42 | """ 43 | value = loader.construct_scalar(node) 44 | match = pattern.findall(value) # to find all env variables in line 45 | if match: 46 | full_value = value 47 | for g in match: 48 | full_value = full_value.replace( 49 | f'${{{g}}}', os.environ.get(g, g) 50 | ) 51 | #DEBUG 52 | print(full_value) 53 | return full_value 54 | #DEBUG 55 | print(value) 56 | return value 57 | 58 | loader.add_constructor(tag, constructor_env_variables) 59 | 60 | if path: 61 | with open(path) as conf_data: 62 | return yaml.load(conf_data, Loader=loader) 63 | elif data: 64 | return yaml.load(data, Loader=loader) 65 | else: 66 | raise ValueError('Either a path or data should be defined as input') -------------------------------------------------------------------------------- /tutorials/1. Learning Aries, ACA-Py and the Basic Controller/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with SSI development on the Hyperledger Stack 2 | ## A tutorial series using the aries-basic-controller library with ACA-Py 3 | 4 | With docker installed, run the example using ./manage start 5 | 6 | This spins up an aries agent and a notebook for both Alice and Bob. Additionally a local von-network is initialised. 7 | 8 | To access the tutorials in the jupyter notebook run from the **root PyDentity folder**: 9 | 10 | `./scripts/get_URLS.sh` 11 | 12 | This will print out the urls with tokens for all jupyter notebook instances running within docker. 13 | 14 | Navigate to the links and begin the tutorial starting from Part 1. 15 | -------------------------------------------------------------------------------- /tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/4 Advanced Concepts/Part 3 - Out of Band Protocol.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "" 12 | ] 13 | } 14 | ], 15 | "metadata": { 16 | "language_info": { 17 | "codemirror_mode": { 18 | "name": "ipython", 19 | "version": 2 20 | }, 21 | "file_extension": ".py", 22 | "mimetype": "text/x-python", 23 | "name": "python", 24 | "nbconvert_exporter": "python", 25 | "pygments_lexer": "ipython2", 26 | "version": "2.7.6" 27 | } 28 | }, 29 | "nbformat": 4, 30 | "nbformat_minor": 0 31 | } -------------------------------------------------------------------------------- /tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/ledger-api.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Aries Basic Controller\n", 8 | "\n", 9 | "## Ledger API\n", 10 | "\n", 11 | "The ledger API is used for inspecting, registering and rotating keys associated the DIDs stored on the ledger. Note: All issuers of verifiable credentials MUST have a DID registered on the ledger. The test network that can be viewed here - http://localhost:9000 does not have protected write access, anyone can write DIDs to the ledger. On production ledgers such as the Sovrin network this is not the case, only existing DIDs with specific roles are able to write new DIDs to the ledger." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "%autoawait\n", 21 | "import time\n", 22 | "import asyncio" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "from aries_basic_controller.aries_controller import AriesAgentController\n", 32 | " \n", 33 | "WEBHOOK_HOST = \"0.0.0.0\"\n", 34 | "WEBHOOK_PORT = 8022\n", 35 | "WEBHOOK_BASE = \"\"\n", 36 | "ADMIN_URL = \"http://alice-agent:8021\"\n", 37 | "\n", 38 | "# WARNING: You should use environment variables for this\n", 39 | "# TODO: Make env variables accessible through juypter notebooks\n", 40 | "API_KEY = \"alice_api_123456789\"\n", 41 | "\n", 42 | "# Based on the aca-py agent you wish to control\n", 43 | "agent_controller = AriesAgentController(admin_url=ADMIN_URL, api_key=API_KEY)" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "agent_controller.init_webhook_server(webhook_host=WEBHOOK_HOST,\n", 53 | " webhook_port=WEBHOOK_PORT,\n", 54 | " webhook_base=WEBHOOK_BASE)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "### Define DID interested in\n", 62 | "\n", 63 | "Alice's DID (nym) is determined by the wallet seed found within the manage file. This is then written to the ledger as part of the docker startup process." 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "alice_nym = \"PQRXDxdGqQGSZ8z69p4xZP\"" 73 | ] 74 | }, 75 | { 76 | "cell_type": "markdown", 77 | "metadata": {}, 78 | "source": [ 79 | "## Fetch Verkey for DID\n", 80 | "\n", 81 | "The verkey can be used to generate the public key associated with the DID" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "alice_verkey = await agent_controller.ledger.get_did_verkey(alice_nym)\n", 91 | "print(alice_verkey)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "## Fetch Endpoint for DID from the Ledger\n", 99 | "\n", 100 | "The endpoint for a DID stored on the ledger can be used to communicate with the entity in control of it. " 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "alice_endpoint = await agent_controller.ledger.get_did_endpoint(alice_nym)\n", 110 | "print(alice_endpoint)" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "## Create a new DID and Register on Ledger" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "response = await agent_controller.wallet.create_did()\n", 127 | "\n", 128 | "did_object = response['result']\n", 129 | "print(\"DID\", did_object)" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "metadata": {}, 136 | "outputs": [], 137 | "source": [ 138 | "result = await agent_controller.ledger.register_nym(did_object['did'], did_object['verkey'])\n", 139 | "print(result)" 140 | ] 141 | }, 142 | { 143 | "cell_type": "markdown", 144 | "metadata": {}, 145 | "source": [ 146 | "## End of Tutorial\n", 147 | "\n", 148 | "Be sure to terminate the controller" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "response = await agent_controller.terminate()\n", 158 | "print(response)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [] 167 | } 168 | ], 169 | "metadata": { 170 | "kernelspec": { 171 | "display_name": "Python 3", 172 | "language": "python", 173 | "name": "python3" 174 | }, 175 | "language_info": { 176 | "codemirror_mode": { 177 | "name": "ipython", 178 | "version": 3 179 | }, 180 | "file_extension": ".py", 181 | "mimetype": "text/x-python", 182 | "name": "python", 183 | "nbconvert_exporter": "python", 184 | "pygments_lexer": "ipython3", 185 | "version": "3.8.5" 186 | } 187 | }, 188 | "nbformat": 4, 189 | "nbformat_minor": 4 190 | } 191 | -------------------------------------------------------------------------------- /tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/server-api.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Aries Basic Controller\n", 8 | "\n", 9 | "## Server" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "%autoawait\n", 19 | "import time\n", 20 | "import asyncio\n", 21 | "from aries_basic_controller.aries_controller import AriesAgentController\n", 22 | " \n", 23 | "WEBHOOK_HOST = \"0.0.0.0\"\n", 24 | "WEBHOOK_PORT = 8022\n", 25 | "WEBHOOK_BASE = \"\"\n", 26 | "ADMIN_URL = \"http://alice-agent:8021\"\n", 27 | "API_KEY = \"alice_api_123456789\"\n", 28 | "\n", 29 | "# Based on the aca-py agent you wish to control\n", 30 | "agent_controller = AriesAgentController(admin_url=ADMIN_URL, api_key=API_KEY)" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": null, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "agent_controller.init_webhook_server(webhook_host=WEBHOOK_HOST,\n", 40 | " webhook_port=WEBHOOK_PORT,\n", 41 | " webhook_base=WEBHOOK_BASE)" 42 | ] 43 | }, 44 | { 45 | "cell_type": "markdown", 46 | "metadata": {}, 47 | "source": [ 48 | "## Get Plugins\n", 49 | "\n", 50 | "This function shows you the set of protocols your aries agent supports." 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "plugins = await agent_controller.server.get_plugins()\n", 60 | "print(plugins)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "## Get Status\n", 68 | "\n", 69 | "This function provides some information about the current status of the agent. It's versions and current tasks done to date." 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "status = await agent_controller.server.get_status()\n", 79 | "print(status)" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "## Reset Status\n", 87 | "\n", 88 | "This can be used to reset the current status of the agent. \n", 89 | "\n", 90 | "**Note: Not actually sure what this does. Would expect it to revert the number of tasks the get_status() method says the agent has done**" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "reset = await agent_controller.server.reset_status()\n", 100 | "print(reset)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "status = await agent_controller.server.get_status()\n", 110 | "print(status)" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "## Get Features\n", 118 | "\n", 119 | "Returns a list of did comm protocol features the agent supports (I think)" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [ 128 | "features = await agent_controller.server.get_features()\n", 129 | "print(features)" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "## End of Tutorial\n", 137 | "\n", 138 | "Be sure to terminate the controller" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [ 147 | "response = await agent_controller.terminate()\n", 148 | "print(response)" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [] 157 | } 158 | ], 159 | "metadata": { 160 | "kernelspec": { 161 | "display_name": "Python 3", 162 | "language": "python", 163 | "name": "python3" 164 | }, 165 | "language_info": { 166 | "codemirror_mode": { 167 | "name": "ipython", 168 | "version": 3 169 | }, 170 | "file_extension": ".py", 171 | "mimetype": "text/x-python", 172 | "name": "python", 173 | "nbconvert_exporter": "python", 174 | "pygments_lexer": "ipython3", 175 | "version": "3.8.5" 176 | } 177 | }, 178 | "nbformat": 4, 179 | "nbformat_minor": 4 180 | } 181 | -------------------------------------------------------------------------------- /tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/alice/APIs/wallet_api.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "pycharm": { 7 | "name": "#%% md\n" 8 | } 9 | }, 10 | "source": [ 11 | "# Aries Basic Controller Example\n", 12 | "## Wallet Api\n", 13 | "\n", 14 | "Note this will typically be used by other functionality within the controller. Essentially enables the creation an storage of public key material." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "pycharm": { 22 | "name": "#%%\n" 23 | } 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "%autoawait\n", 28 | "import time\n", 29 | "import asyncio" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "metadata": { 36 | "pycharm": { 37 | "name": "#%%\n" 38 | } 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "from aries_basic_controller.aries_controller import AriesAgentController\n", 43 | " \n", 44 | "WEBHOOK_HOST = \"0.0.0.0\"\n", 45 | "WEBHOOK_PORT = 8022\n", 46 | "WEBHOOK_BASE = \"\"\n", 47 | "ADMIN_URL = \"http://alice-agent:8021\"\n", 48 | "API_KEY = \"alice_api_123456789\"\n", 49 | "\n", 50 | "# Based on the aca-py agent you wish to control\n", 51 | "agent_controller = AriesAgentController(admin_url=ADMIN_URL, api_key=API_KEY)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "agent_controller.init_webhook_server(webhook_host=WEBHOOK_HOST,\n", 61 | " webhook_port=WEBHOOK_PORT,\n", 62 | " webhook_base=WEBHOOK_BASE)" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "## Create a DID" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": { 76 | "pycharm": { 77 | "name": "#%%response = await agent_controller.wallet.create_did()\n" 78 | } 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "response = await agent_controller.wallet.create_did()\n", 83 | "\n", 84 | "did_object = response['result']\n", 85 | "print(\"DID\", did_object)" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "## Get all DIDs" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": null, 98 | "metadata": { 99 | "pycharm": { 100 | "name": "#%%\n" 101 | } 102 | }, 103 | "outputs": [], 104 | "source": [ 105 | "response = await agent_controller.wallet.get_dids()\n", 106 | "print(response)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "## Get current public DID" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": { 120 | "pycharm": { 121 | "name": "#%%\n" 122 | } 123 | }, 124 | "outputs": [], 125 | "source": [ 126 | "response = await agent_controller.wallet.get_public_did()\n", 127 | "print(response)" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "## Assign new public DID for agent\n", 135 | "\n", 136 | "**Note this will only work if the DID has been previously registered on the ledger. For now this can be done manually by copying the did and verkey values from the above did_object into the web interface running [here](http://localhost:9000)**\n" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": { 143 | "pycharm": { 144 | "name": "#%%\n" 145 | } 146 | }, 147 | "outputs": [], 148 | "source": [ 149 | "response = await agent_controller.wallet.assign_public_did(did_object['did'])\n", 150 | "print(response)" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "## There are also endpoints implemented for getting and setting tags on particular credential definitions. Not too sure how this is used so no walkthrough for now." 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": { 163 | "pycharm": { 164 | "name": "#%%\n" 165 | } 166 | }, 167 | "source": [ 168 | "## End of Tutorial\n", 169 | "\n", 170 | "Be sure to terminate the controller so you can run another tutorial." 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "response = await agent_controller.terminate()\n", 180 | "print(response)" 181 | ] 182 | } 183 | ], 184 | "metadata": { 185 | "kernelspec": { 186 | "display_name": "Python 3", 187 | "language": "python", 188 | "name": "python3" 189 | }, 190 | "language_info": { 191 | "codemirror_mode": { 192 | "name": "ipython", 193 | "version": 3 194 | }, 195 | "file_extension": ".py", 196 | "mimetype": "text/x-python", 197 | "name": "python", 198 | "nbconvert_exporter": "python", 199 | "pygments_lexer": "ipython3", 200 | "version": "3.8.5" 201 | }, 202 | "pycharm": { 203 | "stem_cell": { 204 | "cell_type": "raw", 205 | "metadata": { 206 | "collapsed": false 207 | }, 208 | "source": [] 209 | } 210 | } 211 | }, 212 | "nbformat": 4, 213 | "nbformat_minor": 4 214 | } 215 | -------------------------------------------------------------------------------- /tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/3 Messages/Part 1 - Basic Message.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Basic Message - Bob\n", 8 | "\n", 9 | "**Should be run alongside Alice.**" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "%autoawait\n", 19 | "import time\n", 20 | "import asyncio" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "from aries_basic_controller.aries_controller import AriesAgentController\n", 30 | " \n", 31 | "WEBHOOK_HOST = \"0.0.0.0\"\n", 32 | "WEBHOOK_PORT = 8052\n", 33 | "WEBHOOK_BASE = \"\"\n", 34 | "ADMIN_URL = \"http://bob-agent:8051\"\n", 35 | "\n", 36 | "agent_controller = AriesAgentController(admin_url=ADMIN_URL)" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "agent_controller.init_webhook_server(webhook_host=WEBHOOK_HOST,\n", 46 | " webhook_port=WEBHOOK_PORT,\n", 47 | " webhook_base=WEBHOOK_BASE)" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "## Setup a listener for the basicmessages topic\n", 55 | "\n", 56 | "This is emitted using PyPubSub when the controller receives a basicmessages webhook from the agent. This happens everytime the agent receives a basicmessage.\n", 57 | "\n", 58 | "Note, in this instance the handler responds to any basic message with a message of it's own." 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "def messages_handler(payload):\n", 68 | " connection_id = payload[\"connection_id\"]\n", 69 | " asyncio.get_event_loop().create_task(agent_controller.messaging.send_message(connection_id, \"This is a response from Bob\"))\n", 70 | " print(\"Handle message\", payload, connection_id)\n", 71 | "\n", 72 | "\n", 73 | "message_listener = {\n", 74 | " \"handler\": messages_handler,\n", 75 | " \"topic\": \"basicmessages\"\n", 76 | "}\n", 77 | "\n", 78 | "loop = asyncio.get_event_loop()\n", 79 | "loop.create_task(agent_controller.listen_webhooks())\n", 80 | "\n", 81 | "agent_controller.register_listeners([message_listener], defaults=True)" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "## Verify an Active Connection Exists\n", 89 | "\n", 90 | "This should have been established as part of the setup script between the two agents Alice and Bob." 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "response = await agent_controller.connections.get_connections()\n", 100 | "results = response['results']\n", 101 | "print(\"Results : \", results)\n", 102 | "if len(results) > 0:\n", 103 | " connection = response['results'][0]\n", 104 | " print(\"Connection :\", connection)\n", 105 | " if connection['state'] == 'active': \n", 106 | " connection_id = connection[\"connection_id\"]\n", 107 | " print(\"Active Connection ID : \", connection_id)\n", 108 | "else:\n", 109 | " print(\"You must create a connection\")\n", 110 | " " 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "## Send a Basic Message over DIDComm to Bob\n", 118 | "\n", 119 | "See [aries-rfc](https://github.com/hyperledger/aries-rfcs/tree/master/features/0095-basic-message)\n", 120 | "\n", 121 | "You can send as many messages as you want, if you are running the [basic-message tutorial on Alice's notebook](http://localhost:8888/lab/tree/3%20Messages/Part%201%20-%20Basic%20Message.ipynb) these will be received and printed by the message handler." 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "basic_message = \"Hello from Bob\"\n", 131 | "response = await agent_controller.messaging.send_message(connection_id, basic_message)\n", 132 | "print(\"BASIC MESSAGE - Bob -> Alice\")\n", 133 | "print(response)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "## End of Tutorial\n", 141 | "\n", 142 | "Be sure to terminate the controller so you can run another tutorial." 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": null, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "response = await agent_controller.terminate()\n", 152 | "print(response)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [] 161 | } 162 | ], 163 | "metadata": { 164 | "kernelspec": { 165 | "display_name": "Python 3", 166 | "language": "python", 167 | "name": "python3" 168 | }, 169 | "language_info": { 170 | "codemirror_mode": { 171 | "name": "ipython", 172 | "version": 3 173 | }, 174 | "file_extension": ".py", 175 | "mimetype": "text/x-python", 176 | "name": "python", 177 | "nbconvert_exporter": "python", 178 | "pygments_lexer": "ipython3", 179 | "version": "3.8.5" 180 | } 181 | }, 182 | "nbformat": 4, 183 | "nbformat_minor": 4 184 | } 185 | -------------------------------------------------------------------------------- /tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/4 Advanced Concepts/Part 3 - Out of Band Protocol.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Out of Band Protocol - Receiver\n", 8 | "\n", 9 | "The out of band protocol allows agents to exchange messages without requiring a DIDComm channel. This can be used to establish a connection, request a presentation or issue a credential. \n", 10 | "\n", 11 | "The RFC is described [here](https://github.com/hyperledger/aries-rfcs/tree/master/features/0434-outofband)\n", 12 | "\n", 13 | "Begin in the [sender notebook](http://localhost:8889/lab/tree/4%20Advanced%20Concepts/Part%203%20-%20Out%20of%20Band%20Protocol.ipynb)." 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## 5. Initialise the Controller" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "%autoawait\n", 30 | "import time\n", 31 | "import asyncio\n", 32 | "\n", 33 | "from aries_basic_controller.aries_controller import AriesAgentController\n", 34 | " \n", 35 | "WEBHOOK_HOST = \"0.0.0.0\"\n", 36 | "WEBHOOK_PORT = 8052\n", 37 | "WEBHOOK_BASE = \"\"\n", 38 | "ADMIN_URL = \"http://bob-agent:8051\"\n", 39 | "\n", 40 | "agent_controller = AriesAgentController(admin_url=ADMIN_URL)" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "agent_controller.init_webhook_server(webhook_host=WEBHOOK_HOST,\n", 50 | " webhook_port=WEBHOOK_PORT,\n", 51 | " webhook_base=WEBHOOK_BASE)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "## 6. Configure Connection Listeners" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "loop = asyncio.get_event_loop()\n", 68 | "loop.create_task(agent_controller.listen_webhooks())\n", 69 | "\n", 70 | "def connections_handler(payload):\n", 71 | " print(\"Connection Webhook : \", payload)\n", 72 | " \n", 73 | "connections_listener = {\n", 74 | " \"topic\": \"connections\",\n", 75 | " \"handler\": connections_handler\n", 76 | "}\n", 77 | "\n", 78 | "\n", 79 | "agent_controller.register_listeners([connections_listener], defaults=True)" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "## 7. Receive Out of Band Invitation\n", 87 | "\n", 88 | "**You should have copied this from the Sender notebook (Step 3)**" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "import json\n", 98 | "oob_invite = {'@type': 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/out-of-band/1.0/invitation', '@id': 'e536ed97-2e32-4133-bf20-7ce998739267', 'label': 'Alice', 'handshake_protocols': ['https://didcomm.org/connections/1.0/invitation', 'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation'], 'request~attach': [], 'service': [{'id': '#inline', 'type': 'did-communication', 'recipientKeys': ['did:key:z6Mkn6wscbEJxHZ4Fnf31DwuSfADcWqh6Ncc3fpNJTYHYZHP'], 'routingKeys': [], 'serviceEndpoint': 'http://172.17.0.1:8020'}]}\n", 99 | "response = await agent_controller.oob.receive_invitation(oob_invite)\n", 100 | "print(response)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "## 8. Get Connection" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "connection_id = response[\"connection_id\"]\n", 117 | "print(connection_id)\n", 118 | "\n", 119 | "connection = await agent_controller.connections.get_connection(connection_id)\n", 120 | "\n", 121 | "print(connection)\n", 122 | "print(\"Connection State : \", connection[\"state\"])" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "metadata": {}, 128 | "source": [ 129 | "## 9. Send Trust Ping\n", 130 | "\n", 131 | "This moves the connection to the active state" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "await agent_controller.messaging.trust_ping(connection_id, \"active\")" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "## End Tutorial\n" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [ 156 | "await agent_controller.terminate()" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [] 165 | } 166 | ], 167 | "metadata": { 168 | "kernelspec": { 169 | "display_name": "Python 3", 170 | "language": "python", 171 | "name": "python3" 172 | }, 173 | "language_info": { 174 | "codemirror_mode": { 175 | "name": "ipython", 176 | "version": 3 177 | }, 178 | "file_extension": ".py", 179 | "mimetype": "text/x-python", 180 | "name": "python", 181 | "nbconvert_exporter": "python", 182 | "pygments_lexer": "ipython3", 183 | "version": "3.8.5" 184 | } 185 | }, 186 | "nbformat": 4, 187 | "nbformat_minor": 4 188 | } 189 | -------------------------------------------------------------------------------- /tutorials/1. Learning Aries, ACA-Py and the Basic Controller/notebooks/bob/APIs/credential-api.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Aries Basic Controller - Credential API\n", 8 | "\n", 9 | "## First run through credential issuance flow in the [issuer](http://localhost:8888/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) and [holder](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) notebooks so that Bob's agent has a credential stored in it's wallet.\n", 10 | "\n", 11 | "The credential api enables operations to be performed on the credentials currently stored within an agents wallet." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## Initialise The Controller" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "%autoawait\n", 28 | "import time\n", 29 | "import asyncio\n", 30 | "\n", 31 | "from aries_basic_controller.aries_controller import AriesAgentController\n", 32 | " \n", 33 | "WEBHOOK_HOST = \"0.0.0.0\"\n", 34 | "WEBHOOK_PORT = 8052\n", 35 | "WEBHOOK_BASE = \"\"\n", 36 | "ADMIN_URL = \"http://bob-agent:8051\"\n", 37 | "\n", 38 | "agent_controller = AriesAgentController(admin_url=ADMIN_URL)" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "agent_controller.init_webhook_server(webhook_host=WEBHOOK_HOST,\n", 48 | " webhook_port=WEBHOOK_PORT,\n", 49 | " webhook_base=WEBHOOK_BASE)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "## Get All Credentials\n", 57 | "\n", 58 | "### Optional Parameters\n", 59 | "\n", 60 | "* wql_query (string): Some query language, not too sure how it works.\n", 61 | "* count (int): Limit the number of credentials returned\n", 62 | "* start (int): Return credentials after a start index" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "response = await agent_controller.credentials.get_all()\n", 72 | "results = response[\"results\"]\n", 73 | "print(results)\n", 74 | "\n", 75 | "credential_id = results[0][\"referent\"]\n", 76 | "print(credential_id)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "## Get By ID\n", 84 | "\n", 85 | "Fetch a credential by it's ID. This is a string value that you gave to the credential when initially storing it. See [holder notbook](http://localhost:8889/lab/tree/2%20Credentials/Part%202%20-%20Issue%20Credential.ipynb) step 12." 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [ 94 | "credential = await agent_controller.credentials.get_by_id(credential_id)\n", 95 | "print(credential)" 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": {}, 101 | "source": [ 102 | "## Get MIME Types\n", 103 | "\n", 104 | "This should return the mime types of a particular credential, if defined. (I think)" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "mime_types = await agent_controller.credentials.get_credential_mime_types(credential_id)\n", 114 | "print(mime_types)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "## Remove Credential\n", 122 | "\n", 123 | "A credential can be removed from the wallet of an agent meaning it will no longer be able to generate proofs from it." 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "response = await agent_controller.credentials.remove_credential(credential_id)\n", 133 | "response = await agent_controller.credentials.get_all()\n", 134 | "print(response)" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "## End of Tutorial\n", 142 | "\n", 143 | "Be sure to terminate the controller so you can run another tutorial" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [ 152 | "response = await agent_controller.terminate()" 153 | ] 154 | } 155 | ], 156 | "metadata": { 157 | "kernelspec": { 158 | "display_name": "Python 3", 159 | "language": "python", 160 | "name": "python3" 161 | }, 162 | "language_info": { 163 | "codemirror_mode": { 164 | "name": "ipython", 165 | "version": 3 166 | }, 167 | "file_extension": ".py", 168 | "mimetype": "text/x-python", 169 | "name": "python", 170 | "nbconvert_exporter": "python", 171 | "pygments_lexer": "ipython3", 172 | "version": "3.8.5" 173 | } 174 | }, 175 | "nbformat": 4, 176 | "nbformat_minor": 4 177 | } 178 | -------------------------------------------------------------------------------- /tutorials/2. Public Ledgers and Mobile Wallets/README.md: -------------------------------------------------------------------------------- 1 | # Public ledgers and mobile wallets example 2 | 3 | This example takes you through the process of moving from a local development environment into a more realistic implementation of SSI with credential schema and definitions written to the public Sovrin StagingNet. 4 | 5 | In this example, **you** will play the role of holder using a mobile agent of your choice. The notebooks represent an issuer and a verifier, the example takes you through issuing a credential to your wallet using the issuer notebook and then presenting attributes from this credential to the verifier on a different notebook. This begins to show the power of this architecture, the individual becomes the point of interoperability between systems for data about them. 6 | 7 | ## Notebooks: 8 | 9 | * [Issuer](http://localhost:8888/lab) 10 | * [Verifier](http://localhost:8889/lab) 11 | 12 | In order to obtain the tokens required to access the notebooks, please run `./scripts/get_URLS.sh` in the root folder of the project (`/PyDentity`). If you run this you can also just click the links genrated by the script instead of copy-pasting the token. 13 | -------------------------------------------------------------------------------- /tutorials/2. Public Ledgers and Mobile Wallets/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | ngrok-verifier: 4 | image: wernight/ngrok 5 | command: ngrok http verifier-agent:${VERIFIER_HTTP_PORT} --log stdout 6 | networks: 7 | - indy_demo 8 | verifier-agent: 9 | build: 10 | context: ../../ 11 | dockerfile: dockerfiles/agents/Dockerfile.ngrok 12 | environment: 13 | - NGROK_NAME=ngrok-verifier 14 | - ADMIN_URL=http://verifier-agent:${VERIFIER_ADMIN_PORT} 15 | - AGENT_NAME=${VERIFIER_AGENT_NAME} 16 | - ADMIN_PORT=${VERIFIER_ADMIN_PORT} 17 | - HTTP_PORT=${VERIFIER_HTTP_PORT} 18 | - ACAPY_WEBHOOK_URL=${VERIFIER_WEBHOOK_URL} 19 | - AGENT_ENDPOINT=${VERIFIER_AGENT_ENDPOINT} 20 | - WALLET_SEED=${VERIFIER_WALLET_SEED} 21 | - WALLET_NAME=${VERIFIER_WALLET_NAME} 22 | - WALLET_KEY=${VERIFIER_WALLET_KEY} 23 | - WALLET_TYPE=${WALLET_TYPE} 24 | - ACAPY_ADMIN_INSECURE_MODE=${VERIFIER_ADMIN_SECURE} 25 | - GENESIS_URL=${GENESIS_URL} 26 | ports: 27 | - ${VERIFIER_HTTP_PORT}:${VERIFIER_HTTP_PORT} 28 | - ${VERIFIER_ADMIN_PORT}:${VERIFIER_ADMIN_PORT} 29 | networks: 30 | - indy_demo 31 | 32 | ngrok-issuer: 33 | image: wernight/ngrok 34 | command: ngrok http issuer-agent:${ISSUER_HTTP_PORT} --log stdout 35 | networks: 36 | - indy_demo 37 | issuer-agent: 38 | build: 39 | context: ../../ 40 | dockerfile: dockerfiles/agents/Dockerfile.ngrok 41 | environment: 42 | - NGROK_NAME=ngrok-issuer 43 | - ADMIN_URL=http://issuer-agent:${ISSUER_ADMIN_PORT} 44 | - AGENT_NAME=${ISSUER_AGENT_NAME} 45 | - ADMIN_PORT=${ISSUER_ADMIN_PORT} 46 | - HTTP_PORT=${ISSUER_HTTP_PORT} 47 | - ACAPY_WEBHOOK_URL=${ISSUER_WEBHOOK_URL} 48 | - AGENT_ENDPOINT=${ISSUER_AGENT_ENDPOINT} 49 | - WALLET_SEED=${ISSUER_WALLET_SEED} 50 | - WALLET_NAME=${ISSUER_WALLET_NAME} 51 | - WALLET_KEY=${ISSUER_WALLET_KEY} 52 | - WALLET_TYPE=${WALLET_TYPE} 53 | - ACAPY_ADMIN_INSECURE_MODE=${ISSUER_ADMIN_SECURE} 54 | - GENESIS_URL=${GENESIS_URL} 55 | ports: 56 | - ${ISSUER_HTTP_PORT}:${ISSUER_HTTP_PORT} 57 | - ${ISSUER_ADMIN_PORT}:${ISSUER_ADMIN_PORT} 58 | networks: 59 | - indy_demo 60 | issuer-notebook: 61 | build: 62 | context: ../../ 63 | dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller 64 | args: 65 | - jupyter_port=${ISSUER_JUPYTER_PORT} 66 | depends_on: 67 | - issuer-agent 68 | networks: 69 | - indy_demo 70 | volumes: 71 | - ./notebooks/issuer:/workspace 72 | ports: 73 | - "8888:8888" 74 | - ${ISSUER_WEBHOOK_PORT}:${ISSUER_WEBHOOK_PORT} 75 | verifier-notebook: 76 | build: 77 | context: ../../ 78 | dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller 79 | args: 80 | - jupyter_port=${VERIFIER_JUPYTER_PORT} 81 | depends_on: 82 | - verifier-agent 83 | networks: 84 | - indy_demo 85 | volumes: 86 | - ${PWD}/notebooks/verifier:/workspace 87 | ports: 88 | - "8889:8888" 89 | - ${VERIFIER_WEBHOOK_PORT}:${VERIFIER_WEBHOOK_PORT} 90 | 91 | networks: 92 | indy_demo: 93 | -------------------------------------------------------------------------------- /tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/issuer_agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/issuer/issuer_agent_invite_QRcode.png -------------------------------------------------------------------------------- /tutorials/2. Public Ledgers and Mobile Wallets/notebooks/verifier/verifier_agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/tutorials/2. Public Ledgers and Mobile Wallets/notebooks/verifier/verifier_agent_invite_QRcode.png -------------------------------------------------------------------------------- /tutorials/3. Attachments/README.md: -------------------------------------------------------------------------------- 1 | # Attachment Protocol Tutorial -------------------------------------------------------------------------------- /tutorials/3. Attachments/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | alice-agent: 4 | image: alice-protocol:agent 5 | build: 6 | context: ../../ 7 | dockerfile: dockerfiles/agents/Dockerfile.attachmentprotocol 8 | environment: 9 | - ADMIN_URL=http://alice-agent:${ALICE_ADMIN_PORT} 10 | - AGENT_NAME=${ALICE_AGENT_NAME} 11 | - ADMIN_PORT=${ALICE_ADMIN_PORT} 12 | - HTTP_PORT=${ALICE_HTTP_PORT} 13 | - ACAPY_WEBHOOK_URL=${ALICE_WEBHOOK_URL} 14 | - AGENT_ENDPOINT=${ALICE_AGENT_ENDPOINT} 15 | - WALLET_SEED=${ALICE_WALLET_SEED} 16 | - WALLET_NAME=${ALICE_WALLET_NAME} 17 | - WALLET_TYPE=${WALLET_TYPE} 18 | - WALLET_KEY=${ALICE_WALLET_KEY} 19 | - ACAPY_ADMIN_API_KEY=${ALICE_API_KEY} 20 | - GENESIS_URL=${GENESIS_URL} 21 | - ACAPY_NO_LEDGER=true 22 | - ACAPY_PLUGIN=attach_protocol.attachment_protocol 23 | ports: 24 | - ${ALICE_HTTP_PORT}:${ALICE_HTTP_PORT} 25 | - ${ALICE_ADMIN_PORT}:${ALICE_ADMIN_PORT} 26 | networks: 27 | - indy_demo 28 | entrypoint: /bin/bash 29 | command: [ 30 | "-c", 31 | " 32 | sleep 5; \ 33 | python3 scripts/parse_yml_env_variables.py -c ./configuration/aries-args-basic.yaml; \ 34 | aca-py start --arg-file /tmp/agent_conf.yml" 35 | ] 36 | bob-agent: 37 | image: bob-protocol:agent 38 | build: 39 | context: ../../ 40 | dockerfile: dockerfiles/agents/Dockerfile.attachmentprotocol 41 | environment: 42 | - ADMIN_URL=http://bob-agent:${BOB_ADMIN_PORT} 43 | - AGENT_NAME=${BOB_AGENT_NAME} 44 | - ADMIN_PORT=${BOB_ADMIN_PORT} 45 | - HTTP_PORT=${BOB_HTTP_PORT} 46 | - ACAPY_WEBHOOK_URL=${BOB_WEBHOOK_URL} 47 | - AGENT_ENDPOINT=${BOB_AGENT_ENDPOINT} 48 | - WALLET_SEED=${BOB_WALLET_SEED} 49 | - WALLET_NAME=${BOB_WALLET_NAME} 50 | - WALLET_TYPE=${WALLET_TYPE} 51 | - WALLET_KEY=${BOB_WALLET_KEY} 52 | - ACAPY_ADMIN_INSECURE_MODE=${BOB_ADMIN_SECURE} 53 | - GENESIS_URL=${GENESIS_URL} 54 | - ACAPY_NO_LEDGER=true 55 | - ACAPY_PLUGIN=attach_protocol.attachment_protocol 56 | ports: 57 | - ${BOB_HTTP_PORT}:${BOB_HTTP_PORT} 58 | - ${BOB_ADMIN_PORT}:${BOB_ADMIN_PORT} 59 | networks: 60 | - indy_demo 61 | entrypoint: /bin/bash 62 | command: [ 63 | "-c", 64 | " 65 | sleep 5; \ 66 | python3 scripts/parse_yml_env_variables.py -c ./configuration/aries-args-basic.yaml; \ 67 | aca-py start --arg-file /tmp/agent_conf.yml" 68 | ] 69 | setup: 70 | build: 71 | context: ../../ 72 | dockerfile: dockerfiles/agents/Dockerfile.createconnection 73 | networks: 74 | - indy_demo 75 | depends_on: 76 | - alice-agent 77 | - bob-agent 78 | # ports: 79 | # - ${ALICE_WEBHOOK_PORT}:${ALICE_WEBHOOK_PORT} 80 | # - ${BOB_WEBHOOK_PORT}:${BOB_WEBHOOK_PORT} 81 | environment: 82 | - ALICE_ADMIN_URL=http://alice-agent:${ALICE_ADMIN_PORT} 83 | - ALICE_WEBHOOK_PORT=${ALICE_WEBHOOK_PORT} 84 | - ALICE_API_KEY=${ALICE_API_KEY} 85 | - ALICE_WEBHOOK_HOST=0.0.0.0 86 | - BOB_ADMIN_URL=http://bob-agent:${BOB_ADMIN_PORT} 87 | - BOB_WEBHOOK_PORT=${BOB_WEBHOOK_PORT} 88 | - BOB_WEBHOOK_HOST=0.0.0.0 89 | entrypoint: /bin/bash 90 | command: 91 | [ 92 | "-c", 93 | "sleep 5; 94 | python ./create_connection.py" 95 | ] 96 | alice-notebook: 97 | build: 98 | context: ../../ 99 | dockerfile: dockerfiles/controllers/Dockerfile.attachmentcontroller 100 | args: 101 | - jupyter_port=${ALICE_JUPYTER_PORT} 102 | depends_on: 103 | - alice-agent 104 | networks: 105 | - indy_demo 106 | volumes: 107 | - ${PWD}/notebooks/alice:/workspace 108 | ports: 109 | - "8888:8888" 110 | - ${ALICE_WEBHOOK_PORT}:${ALICE_WEBHOOK_PORT} 111 | bob-notebook: 112 | build: 113 | context: ../../ 114 | dockerfile: dockerfiles/controllers/Dockerfile.attachmentcontroller 115 | args: 116 | - jupyter_port=${BOB_JUPYTER_PORT} 117 | depends_on: 118 | - bob-agent 119 | networks: 120 | - indy_demo 121 | volumes: 122 | - ${PWD}/notebooks/bob:/workspace 123 | ports: 124 | - "8889:8888" 125 | - ${BOB_WEBHOOK_PORT}:${BOB_WEBHOOK_PORT} 126 | 127 | networks: 128 | indy_demo: 129 | -------------------------------------------------------------------------------- /tutorials/3. Attachments/notebooks/alice/openmined.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/tutorials/3. Attachments/notebooks/alice/openmined.jpg -------------------------------------------------------------------------------- /tutorials/3. Attachments/notebooks/alice/test_file.txt: -------------------------------------------------------------------------------- 1 | This is a test file to attach. -------------------------------------------------------------------------------- /tutorials/3. Attachments/notebooks/bob/attachment.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Attachment Protocol - Bob\n", 8 | "## Role: Receiver\n", 9 | "\n", 10 | "## This notebook should be run alongside [Alice](http://localhost:8888/lab/tree/attachment.ipynb), who will send Bob two attachments.\n", 11 | "\n", 12 | "In this notebook Bob instatiates his controller and sets up a handler for attachment protocol messages, all files he receives he will save in the [received_files folder](http://localhost:8889/tree/received_files). It should currently be empty.\n" 13 | ] 14 | }, 15 | { 16 | "cell_type": "markdown", 17 | "metadata": {}, 18 | "source": [ 19 | "### Intialise Controller" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "%autoawait\n", 29 | "import time\n", 30 | "import asyncio\n", 31 | "import base64\n", 32 | "from attachment_controller.attachment_controller import AttachmentController\n", 33 | " \n", 34 | "WEBHOOK_HOST = \"0.0.0.0\"\n", 35 | "WEBHOOK_PORT = 8052\n", 36 | "WEBHOOK_BASE = \"\"\n", 37 | "ADMIN_URL = \"http://bob-agent:8051\"\n", 38 | "\n", 39 | "# Based on the aca-py agent you wish to control\n", 40 | "attach_controller = AttachmentController(admin_url=ADMIN_URL, webhook_host=WEBHOOK_HOST, webhook_base=WEBHOOK_BASE, webhook_port=WEBHOOK_PORT)" 41 | ] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "metadata": {}, 46 | "source": [ 47 | "### Check if active connection exists" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": null, 53 | "metadata": {}, 54 | "outputs": [], 55 | "source": [ 56 | "response = await attach_controller.connections.get_connections()\n", 57 | "results = response['results']\n", 58 | "print(\"Results : \", results)\n", 59 | "if len(results) > 0:\n", 60 | " connection = response['results'][0]\n", 61 | " print(\"Connection :\", connection)\n", 62 | " if connection['state'] == 'active': \n", 63 | " connection_id = connection[\"connection_id\"]\n", 64 | " print(\"Active Connection ID : \", connection_id)\n", 65 | " else:\n", 66 | " print(\"Connection is still progressing to active state, retry in a few moments\")\n", 67 | "else:\n", 68 | " print(\"You must create a connection\")\n" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "### Instantiate Listener" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "The \"attachment\" event is emitted using PyPubSub when the controller receives a atatchment webhook from the agent. This happens everytime the agent receives an attachment.\n", 83 | "\n", 84 | "In this handler we receive the attachment payload, decode it and save it in the received files folder using the filename provided in the protocol. \n", 85 | "\n", 86 | "Currently, this has been tested for basic text files and for images. Feel free to try additional file types." 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "\n", 96 | "def attach_handler(payload):\n", 97 | " connection_id = payload['connection_id']\n", 98 | " print(\"Handle Attachment\", payload, connection_id)\n", 99 | " print(\"Attachment Received\")\n", 100 | " data = payload['content']\n", 101 | " filename = payload['filename']\n", 102 | " file_content = base64.b64decode(data)\n", 103 | "# fh = open(\"image_received.png\", \"wb\")\n", 104 | "# fh.write(text)\n", 105 | "# fh.close()\n", 106 | " filepath = f\"{filename}\"\n", 107 | " f = open(filepath,'wb')\n", 108 | " f.write(file_content)\n", 109 | " f.close()\n", 110 | "attach_listener = {\n", 111 | " \"handler\": attach_handler,\n", 112 | " \"topic\": \"attachment\"\n", 113 | "}\n", 114 | "loop = asyncio.get_event_loop()\n", 115 | "loop.create_task(attach_controller.listen_webhooks())\n", 116 | "attach_controller.register_listeners([attach_listener], defaults=True)" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "\n", 124 | "# End of Tutorial\n", 125 | "\n", 126 | "Be sure to terminate the controller so you can run another tutorial.\n" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "response = await attach_controller.terminate()\n", 136 | "print(response)" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [] 145 | } 146 | ], 147 | "metadata": { 148 | "kernelspec": { 149 | "display_name": "Python 3", 150 | "language": "python", 151 | "name": "python3" 152 | }, 153 | "language_info": { 154 | "codemirror_mode": { 155 | "name": "ipython", 156 | "version": 3 157 | }, 158 | "file_extension": ".py", 159 | "mimetype": "text/x-python", 160 | "name": "python", 161 | "nbconvert_exporter": "python", 162 | "pygments_lexer": "ipython3", 163 | "version": "3.8.5" 164 | } 165 | }, 166 | "nbformat": 4, 167 | "nbformat_minor": 4 168 | } 169 | -------------------------------------------------------------------------------- /tutorials/4. Multitenancy/README.md: -------------------------------------------------------------------------------- 1 | # Multitenancy Tutorial 2 | 3 | In this tutorial you will learn how to use an ACA-Py instance in multi-tenant mode. This will include: 4 | 5 | * Managing subwallets (create, update, delete) 6 | * Interacting with the ACA-Py instance as a subwallet 7 | * Establishing a mediator 8 | 9 | **Note: This tutorial also uses a postgres db for the wallet storage of the multi-tenant agent. It is highly recommended to have a look through the docker-compose.yml file to see the additional configuration needed for this (see wallets-db service). As well as the additional ACA-Py flags used by the multitenant-agent service** -------------------------------------------------------------------------------- /tutorials/4. Multitenancy/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | ngrok-external: 4 | image: wernight/ngrok 5 | command: ngrok http external-agent:${EXTERNAL_HTTP_PORT} --log stdout 6 | networks: 7 | - indy_demo 8 | external-agent: 9 | build: 10 | context: ../../ 11 | dockerfile: dockerfiles/agents/Dockerfile.ngrok 12 | environment: 13 | - NGROK_NAME=ngrok-external 14 | - ADMIN_URL=http://external-agent:${EXTERNAL_ADMIN_PORT} 15 | - AGENT_NAME=${EXTERNAL_AGENT_NAME} 16 | - ADMIN_PORT=${EXTERNAL_ADMIN_PORT} 17 | - HTTP_PORT=${EXTERNAL_HTTP_PORT} 18 | - ACAPY_WEBHOOK_URL=${EXTERNAL_WEBHOOK_URL} 19 | - AGENT_ENDPOINT=${EXTERNAL_AGENT_ENDPOINT} 20 | - WALLET_SEED=${EXTERNAL_WALLET_SEED} 21 | - WALLET_NAME=${EXTERNAL_WALLET_NAME} 22 | - WALLET_KEY=${EXTERNAL_WALLET_KEY} 23 | - WALLET_TYPE=${WALLET_TYPE} 24 | - ACAPY_ADMIN_INSECURE_MODE=${EXTERNAL_ADMIN_SECURE} 25 | - GENESIS_URL=${GENESIS_URL} 26 | ports: 27 | - ${EXTERNAL_HTTP_PORT}:${EXTERNAL_HTTP_PORT} 28 | - ${EXTERNAL_ADMIN_PORT}:${EXTERNAL_ADMIN_PORT} 29 | networks: 30 | - indy_demo 31 | ngrok-mediator: 32 | image: wernight/ngrok 33 | command: ngrok http mediator-agent:${MEDIATOR_HTTP_PORT} --log stdout 34 | networks: 35 | - indy_demo 36 | mediator-agent: 37 | build: 38 | context: ../../ 39 | dockerfile: dockerfiles/agents/Dockerfile.ngrok 40 | environment: 41 | - NGROK_NAME=ngrok-mediator 42 | - ADMIN_URL=http://mediator-agent:${MEDIATOR_ADMIN_PORT} 43 | - AGENT_NAME=${MEDIATOR_AGENT_NAME} 44 | - ADMIN_PORT=${MEDIATOR_ADMIN_PORT} 45 | - HTTP_PORT=${MEDIATOR_HTTP_PORT} 46 | - ACAPY_WEBHOOK_URL=${MEDIATOR_WEBHOOK_URL} 47 | - AGENT_ENDPOINT=${MEDIATOR_AGENT_ENDPOINT} 48 | - WALLET_SEED=${MEDIATOR_WALLET_SEED} 49 | - WALLET_NAME=${MEDIATOR_WALLET_NAME} 50 | - WALLET_KEY=${MEDIATOR_WALLET_KEY} 51 | - WALLET_TYPE=${WALLET_TYPE} 52 | - ACAPY_ADMIN_INSECURE_MODE=${MEDIATOR_ADMIN_SECURE} 53 | - GENESIS_URL=${GENESIS_URL} 54 | ports: 55 | - ${MEDIATOR_HTTP_PORT}:${MEDIATOR_HTTP_PORT} 56 | - ${MEDIATOR_ADMIN_PORT}:${MEDIATOR_ADMIN_PORT} 57 | networks: 58 | - indy_demo 59 | mediator-notebook: 60 | build: 61 | context: ../../ 62 | dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller 63 | args: 64 | - jupyter_port=${MEDIATOR_JUPYTER_PORT} 65 | depends_on: 66 | - mediator-agent 67 | networks: 68 | - indy_demo 69 | volumes: 70 | - ${PWD}/notebooks/mediator:/workspace 71 | ports: 72 | - ${MEDIATOR_JUPYTER_PORT}:8888 73 | - ${MEDIATOR_WEBHOOK_PORT}:${MEDIATOR_WEBHOOK_PORT} 74 | wallets-db: 75 | image: postgres:11 76 | container_name: wallets-db 77 | command: postgres -c listen_addresses='*' 78 | environment: 79 | POSTGRES_PASSWORD: dbpass 80 | POSTGRES_USER: postgres 81 | POSTGRES_DB: wallets 82 | volumes: 83 | - ./resources/init.sql:/docker-entrypoint-initdb.d/init.sql 84 | networks: 85 | - indy_demo 86 | tty: true 87 | ngrok-multitenant: 88 | image: wernight/ngrok 89 | command: ngrok http multitenant-agent:${MULTITENANT_HTTP_PORT} --log stdout 90 | networks: 91 | - indy_demo 92 | multitenant-agent: 93 | build: 94 | context: ../../ 95 | dockerfile: dockerfiles/agents/Dockerfile.ngrok 96 | environment: 97 | - NGROK_NAME=ngrok-multitenant 98 | - ADMIN_URL=http://multitenant-agent:${MULTITENANT_ADMIN_PORT} 99 | - AGENT_NAME=${MULTITENANT_AGENT_NAME} 100 | - ADMIN_PORT=${MULTITENANT_ADMIN_PORT} 101 | - HTTP_PORT=${MULTITENANT_HTTP_PORT} 102 | - ACAPY_WEBHOOK_URL=${MULTITENANT_WEBHOOK_URL} 103 | - AGENT_ENDPOINT=${MULTITENANT_AGENT_ENDPOINT} 104 | - WALLET_SEED=${MULTITENANT_WALLET_SEED} 105 | - WALLET_NAME=${MULTITENANT_WALLET_NAME} 106 | - WALLET_KEY=${MULTITENANT_WALLET_KEY} 107 | - WALLET_TYPE=${WALLET_TYPE} 108 | - ACAPY_ADMIN_INSECURE_MODE=${MULTITENANT_ADMIN_SECURE} 109 | - GENESIS_URL=${GENESIS_URL} 110 | - ACAPY_MULTITENANT=true 111 | - ACAPY_MULTITENANT_JWT_SECRET="password" 112 | - ACAPY_MULTITENANT_ADMIN=true 113 | - ACAPY_WALLET_STORAGE_TYPE=postgres_storage 114 | - ACAPY_WALLET_STORAGE_CONFIG={"url":"wallets-db:5432","wallet_scheme":"MultiWalletSingleTable"} 115 | - ACAPY_WALLET_STORAGE_CREDS={"account":"postgres","password":"dbpass","admin_account":"postgres","admin_password":"dbpass"} 116 | ports: 117 | - ${MULTITENANT_HTTP_PORT}:${MULTITENANT_HTTP_PORT} 118 | - ${MULTITENANT_ADMIN_PORT}:${MULTITENANT_ADMIN_PORT} 119 | depends_on: 120 | - wallets-db 121 | networks: 122 | - indy_demo 123 | multitenant-notebook: 124 | build: 125 | context: ../../ 126 | dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller 127 | args: 128 | - jupyter_port=${MULTITENANT_JUPYTER_PORT} 129 | depends_on: 130 | - multitenant-agent 131 | networks: 132 | - indy_demo 133 | volumes: 134 | - ./notebooks/multitenant:/workspace 135 | ports: 136 | - "8888:8888" 137 | - ${MULTITENANT_WEBHOOK_PORT}:${MULTITENANT_WEBHOOK_PORT} 138 | external-notebook: 139 | build: 140 | context: ../../ 141 | dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller 142 | args: 143 | - jupyter_port=${EXTERNAL_JUPYTER_PORT} 144 | depends_on: 145 | - external-agent 146 | networks: 147 | - indy_demo 148 | volumes: 149 | - ${PWD}/notebooks/external:/workspace 150 | ports: 151 | - "8889:8888" 152 | - ${EXTERNAL_WEBHOOK_PORT}:${EXTERNAL_WEBHOOK_PORT} 153 | 154 | 155 | 156 | networks: 157 | indy_demo: 158 | -------------------------------------------------------------------------------- /tutorials/4. Multitenancy/resources/init.sql: -------------------------------------------------------------------------------- 1 | -- We could also handle this the with storage plugin itself, but sense this only needs to get run once the very first time using separate sql statements 2 | CREATE TABLE IF NOT EXISTS metadata ( 3 | wallet_id VARCHAR(64) NOT NULL, 4 | value BYTEA NOT NULL, 5 | PRIMARY KEY(wallet_id) 6 | ); 7 | CREATE UNIQUE INDEX IF NOT EXISTS ux_metadata_values ON metadata(wallet_id, value); 8 | CREATE UNIQUE INDEX IF NOT EXISTS ux_metadata_wallet_id ON metadata(wallet_id); 9 | 10 | CREATE TABLE IF NOT EXISTS items( 11 | wallet_id VARCHAR(64) NOT NULL, 12 | id BIGSERIAL NOT NULL, 13 | type BYTEA NOT NULL, 14 | name BYTEA NOT NULL, 15 | value BYTEA NOT NULL, 16 | key BYTEA NOT NULL, 17 | PRIMARY KEY(wallet_id, id) 18 | ); 19 | CREATE UNIQUE INDEX IF NOT EXISTS ux_items_wallet_id_id ON items(wallet_id, id); 20 | CREATE UNIQUE INDEX IF NOT EXISTS ux_items_type_name ON items(wallet_id, type, name); 21 | 22 | CREATE TABLE IF NOT EXISTS tags_encrypted( 23 | wallet_id VARCHAR(64) NOT NULL, 24 | name BYTEA NOT NULL, 25 | value BYTEA NOT NULL, 26 | item_id BIGINT NOT NULL, 27 | PRIMARY KEY(wallet_id, name, item_id), 28 | FOREIGN KEY(wallet_id, item_id) 29 | REFERENCES items(wallet_id, id) 30 | ON DELETE CASCADE 31 | ON UPDATE CASCADE 32 | ); 33 | CREATE INDEX IF NOT EXISTS ix_tags_encrypted_name ON tags_encrypted(wallet_id, name); 34 | CREATE INDEX IF NOT EXISTS ix_tags_encrypted_value ON tags_encrypted(wallet_id, sha256(value)); 35 | CREATE INDEX IF NOT EXISTS ix_tags_encrypted_wallet_id_item_id ON tags_encrypted(wallet_id, item_id); 36 | 37 | CREATE TABLE IF NOT EXISTS tags_plaintext( 38 | wallet_id VARCHAR(64) NOT NULL, 39 | name BYTEA NOT NULL, 40 | value TEXT NOT NULL, 41 | item_id BIGINT NOT NULL, 42 | PRIMARY KEY(wallet_id, name, item_id), 43 | FOREIGN KEY(wallet_id, item_id) 44 | REFERENCES items(wallet_id, id) 45 | ON DELETE CASCADE 46 | ON UPDATE CASCADE 47 | ); 48 | CREATE INDEX IF NOT EXISTS ix_tags_plaintext_name ON tags_plaintext(wallet_id, name); 49 | CREATE INDEX IF NOT EXISTS ix_tags_plaintext_value ON tags_plaintext(wallet_id, value); 50 | CREATE INDEX IF NOT EXISTS ix_tags_plaintext_wallet_id_item_id ON tags_plaintext(wallet_id, item_id); -------------------------------------------------------------------------------- /tutorials/5. OM FoPC Course - Public Key Infrastructures/README.md: -------------------------------------------------------------------------------- 1 | # OpenMined Public Key Infastructure Lesson 2 | 3 | This set of notebooks is part of the OpenMined PKI lesson series. 4 | 5 | To start the tutorial first install the prerequisites: 6 | 7 | ## Requirements 8 | 9 | This project is written in Python and is displayed in jupyter notebooks. 10 | 11 | You need to install: 12 | 1. [Docker](https://docs.docker.com/get-docker/) 13 | 2. [docker-compose](https://docs.docker.com/compose/install/) 14 | 3. The **source-to-image** (s2i) tool is also required to build the docker images used in the demo. S2I can be downloaded [here](https://github.com/openshift/source-to-image). The website gives instructions for installing on other platforms like MACOS, Linux, Windows. 15 | Verify that **s2i** is in your PATH. If not, then edit your PATH and add the directory where **s2i** is installed. The **manage** script will look for the **s2i** executable on your PATH. If it is not found you will get a message asking you to download and set it on your PATH. 16 | - If you are using a Mac and have Homebrew installed, the following command will install s2i: `brew install source-to-image` 17 | - If you are using Linux, go to the [releases](https://github.com/openshift/source-to-image/releases/latest) page and download the correct distribution for your machine. Choose either the linux-386 or the linux-amd64 links for 32 and 64-bit, respectively. Unpack the downloaded tar with `tar -xvf "Release.tar.gz"` 18 | - If you are not sure about your Operating System you can visit [this](https://whatsmyos.com/) and/or follow the instructions. 19 | - You should now see an executable called s2i. Either add the location of s2i to your PATH environment variable, or move it to a pre-existing directory in your PATH. For example, `sudo cp /path/to/s2i /usr/local/bin` will work with most setups. You can test it using `s2i version`. 20 | 21 | Ensure that Docker is running. If it is not try `sudo dockerd` in another terminal. 22 | 23 | 24 | # Start Up 25 | 26 | * First copy the provided example.env files to .env files in each of the actors folders. E.g. `cp actors/datascientist/example.env actors/datascientist/.env` 27 | * These specify the configuration parameters for the agent, including ports and API Keys. 28 | * Then run `./manage start` (From within this tutorial folder) - This calls the `manage` bash script and spins up the docker services defined in `docker-compose.yml` 29 | * Get Notebook Urls using `./scripts/get_URLs` (Must be from Root Repo folder) 30 | 31 | 32 | ## This tutorial has been updated to follow the [Aries-Jupyter-Playground](https://github.com/wip-abramson/aries-jupyter-playground) configuration setup. Which is why it is slightly different from the other projects in this repo. 33 | 34 | 35 | -------------------------------------------------------------------------------- /tutorials/5. OM FoPC Course - Public Key Infrastructures/actors/dataowner/example.env: -------------------------------------------------------------------------------- 1 | 2 | # These are dummy values that can be used to get started quickly 3 | # Replace these with real values when deploying to a real environment 4 | 5 | # For postgres DB which hosts the wallets 6 | #WALLET_DB_HOST=dataowner-wallet-db 7 | #WALLET_DB_PORT=5432 8 | #WALLET_DB_USER=postgres 9 | #WALLET_DB_PASS=dbpass 10 | #WALLET_DB_ADMIN_USER=postgres 11 | #WALLET_DB_ADMIN_PASS=dbpass 12 | 13 | # These values are only used locally by docker-compose to set up the DB container - should match values above 14 | #POSTGRES_PASSWORD=dbpass 15 | #POSTGRES_USER=postgres 16 | #POSTGRES_DB=dataowner_wallet 17 | 18 | # for tails server 19 | # ACAPY_TAILS_SERVER_BASE_URL=http://tails-server:6543 20 | 21 | # for aca-py agent 22 | HTTP_PORT=3020 23 | WEBHOOK_PORT=3010 24 | ADMIN_PORT=3021 25 | ADMIN_URL=http://dataowner-agent:3021 26 | ACAPY_OUTBOUND_TRANSPORT=http 27 | ACAPY_ADMIN=[0.0.0.0,3021] 28 | 29 | # Only used if run ./manage production 30 | # Typically you will want to update 0.0.0.0 to some public IP 31 | ACAPY_ENDPOINT=http://dataowner-agent:3020 32 | 33 | ACAPY_WEBHOOK_URL=http://dataowner-business-logic:3010 34 | ACAPY_ADMIN_API_KEY=adminApiKey 35 | ACAPY_LABEL=DataOwner 36 | ACAPY_WALLET_NAME=Dataowner 37 | ACAPY_WALLET_KEY=dataowner_key 38 | ACAPY_WALLET_TYPE=indy 39 | 40 | #ACAPY_WALLET_STORAGE_TYPE=postgres_storage 41 | #ACAPY_WALLET_STORAGE_CONFIG={"url":"dataowner-wallet-db:5432","wallet_scheme":"MultiWalletSingleTable"} 42 | #ACAPY_WALLET_STORAGE_CREDS={"account":"postgres","password":"dbpass","admin_account":"postgres","admin_password":"dbpass"} 43 | ACAPY_AUTO_PROVISION=true 44 | 45 | 46 | ## Local Network 47 | # ACAPY_GENESIS_FILE=/home/indy/von-local-genesis-txns 48 | 49 | ## BC Gov Greenlight 50 | # ACAPY_GENESIS_URL=http://greenlight.bcovrin.vonx.io/genesis 51 | 52 | ## Sovrin BuilderNet 53 | ACAPY_GENESIS_URL=https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_builder_genesis 54 | 55 | ## Sovrin StagingNet 56 | # ACAPY_GENESIS_URL=https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_sandbox_genesis 57 | 58 | 59 | # Multi-tenant Configuration 60 | # ACAPY_MULTITENANT=true 61 | # ACAPY_MULTITENANT_ADMIN=true 62 | # ACAPY_MULTITENANT_JWT_SECRET=jwtSecret 63 | 64 | # Name of ngrok container if exposing agent endpoint over ngrok 65 | NGROK_NAME= ngrok-dataowner 66 | 67 | 68 | # Optional Helper Configurations - See https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/config/argparse.py 69 | ACAPY_AUTO_ACCEPT_INVITES=true 70 | ACAPY_AUTO_ACCEPT_REQUESTS=true 71 | ACAPY_AUTO_PING_CONNECTION=true 72 | ACAPY_AUTO_RESPOND_MESSAGES=true 73 | ACAPY_AUTO_RESPOND_CREDENTIAL_PROPOSAL=true 74 | ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER=true 75 | ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST=true 76 | ACAPY_AUTO_RESPOND_PRESENTATION_PROPOSAL=true 77 | ACAPY_AUTO_RESPOND_PRESENTATION_REQUEST=true 78 | ACAPY_AUTO_STORE_CREDENTIAL=true 79 | ACAPY_AUTO_VERIFY_PRESENTATION=true 80 | ACAPY_PRESERVE_EXCHANGE_RECORDS=true 81 | 82 | 83 | # Optional Trace Arguments 84 | # ACAPY_TRACE=true 85 | # ACAPY_TRACE_TARGET=log 86 | # ACAPY_TRACE_LABEL=Issuer 87 | 88 | # Timing arguments 89 | # ACAPY_TIMING=true 90 | # ACAPY_TIMING_LOG=/home/indy/logs/timing.txt 91 | 92 | # Debug Arguments 93 | # ACAPY_DEBUG=true 94 | 95 | ACAPY_LOG_LEVEL=debug 96 | # ACAPY_LOG_FILE=/home/indy/logs/agent_logs.txt -------------------------------------------------------------------------------- /tutorials/5. OM FoPC Course - Public Key Infrastructures/actors/datascientist/example.env: -------------------------------------------------------------------------------- 1 | 2 | # These are dummy values that can be used to get started quickly 3 | # Replace these with real values when deploying to a real environment 4 | 5 | # For postgres DB which hosts the wallets 6 | #WALLET_DB_HOST=datascientist-wallet-db 7 | #WALLET_DB_PORT=5432 8 | #WALLET_DB_USER=postgres 9 | #WALLET_DB_PASS=dbpass 10 | #WALLET_DB_ADMIN_USER=postgres 11 | #WALLET_DB_ADMIN_PASS=dbpass 12 | 13 | # These values are only used locally by docker-compose to set up the DB container - should match values above 14 | #POSTGRES_PASSWORD=dbpass 15 | #POSTGRES_USER=postgres 16 | #POSTGRES_DB=datascientist_wallet 17 | 18 | # for tails server 19 | # ACAPY_TAILS_SERVER_BASE_URL=http://tails-server:6543 20 | 21 | # for aca-py agent 22 | HTTP_PORT=3020 23 | WEBHOOK_PORT=3010 24 | ADMIN_PORT=3021 25 | ADMIN_URL=http://datascientist-agent:3021 26 | ACAPY_OUTBOUND_TRANSPORT=http 27 | ACAPY_ADMIN=[0.0.0.0,3021] 28 | 29 | # Only used if run ./manage production 30 | # Typically you will want to update 0.0.0.0 to some public IP 31 | ACAPY_ENDPOINT=http://datascientist-agent:3020 32 | 33 | ACAPY_WEBHOOK_URL=http://datascientist-business-logic:3010 34 | ACAPY_ADMIN_API_KEY=adminApiKey 35 | ACAPY_LABEL=DataScientist 36 | ACAPY_WALLET_NAME=Datascientist 37 | ACAPY_WALLET_KEY=datascientist_key 38 | ACAPY_WALLET_TYPE=indy 39 | 40 | #ACAPY_WALLET_STORAGE_TYPE=postgres_storage 41 | #ACAPY_WALLET_STORAGE_CONFIG={"url":"datascientist-wallet-db:5432","wallet_scheme":"MultiWalletSingleTable"} 42 | #ACAPY_WALLET_STORAGE_CREDS={"account":"postgres","password":"dbpass","admin_account":"postgres","admin_password":"dbpass"} 43 | ACAPY_AUTO_PROVISION=true 44 | 45 | 46 | ## Local Network 47 | # ACAPY_GENESIS_FILE=/home/indy/von-local-genesis-txns 48 | 49 | ## BC Gov Greenlight 50 | # ACAPY_GENESIS_URL=http://greenlight.bcovrin.vonx.io/genesis 51 | 52 | ## Sovrin BuilderNet 53 | ACAPY_GENESIS_URL=https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_builder_genesis 54 | 55 | ## Sovrin StagingNet 56 | # ACAPY_GENESIS_URL=https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_sandbox_genesis 57 | 58 | 59 | # Multi-tenant Configuration 60 | # ACAPY_MULTITENANT=true 61 | # ACAPY_MULTITENANT_ADMIN=true 62 | # ACAPY_MULTITENANT_JWT_SECRET=jwtSecret 63 | 64 | # Name of ngrok container if exposing agent endpoint over ngrok 65 | NGROK_NAME= ngrok-datascientist 66 | 67 | 68 | # Optional Helper Configurations - See https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/config/argparse.py 69 | ACAPY_AUTO_ACCEPT_INVITES=true 70 | ACAPY_AUTO_ACCEPT_REQUESTS=true 71 | ACAPY_AUTO_PING_CONNECTION=true 72 | ACAPY_AUTO_RESPOND_MESSAGES=true 73 | ACAPY_AUTO_RESPOND_CREDENTIAL_PROPOSAL=true 74 | ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER=true 75 | ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST=true 76 | ACAPY_AUTO_RESPOND_PRESENTATION_PROPOSAL=true 77 | ACAPY_AUTO_RESPOND_PRESENTATION_REQUEST=true 78 | ACAPY_AUTO_STORE_CREDENTIAL=true 79 | ACAPY_AUTO_VERIFY_PRESENTATION=true 80 | ACAPY_PRESERVE_EXCHANGE_RECORDS=true 81 | 82 | 83 | # Optional Trace Arguments 84 | # ACAPY_TRACE=true 85 | # ACAPY_TRACE_TARGET=log 86 | # ACAPY_TRACE_LABEL=Issuer 87 | 88 | # Timing arguments 89 | # ACAPY_TIMING=true 90 | # ACAPY_TIMING_LOG=/home/indy/logs/timing.txt 91 | 92 | # Debug Arguments 93 | # ACAPY_DEBUG=true 94 | 95 | ACAPY_LOG_LEVEL=debug 96 | # ACAPY_LOG_FILE=/home/indy/logs/agent_logs.txt -------------------------------------------------------------------------------- /tutorials/5. OM FoPC Course - Public Key Infrastructures/actors/om_authority/example.env: -------------------------------------------------------------------------------- 1 | 2 | # These are dummy values that can be used to get started quickly 3 | # Replace these with real values when deploying to a real environment 4 | 5 | # For postgres DB which hosts the wallets 6 | #WALLET_DB_HOST=om-authority-wallet-db 7 | #WALLET_DB_PORT=5432 8 | #WALLET_DB_USER=postgres 9 | #WALLET_DB_PASS=dbpass 10 | #WALLET_DB_ADMIN_USER=postgres 11 | #WALLET_DB_ADMIN_PASS=dbpass 12 | 13 | # These values are only used locally by docker-compose to set up the DB container - should match values above 14 | #POSTGRES_PASSWORD=dbpass 15 | #POSTGRES_USER=postgres 16 | #POSTGRES_DB=om-authority_wallet 17 | 18 | # for tails server 19 | # ACAPY_TAILS_SERVER_BASE_URL=http://tails-server:6543 20 | 21 | # for aca-py agent 22 | HTTP_PORT=3020 23 | WEBHOOK_PORT=3010 24 | ADMIN_PORT=3021 25 | ADMIN_URL=http://om-authority-agent:3021 26 | ACAPY_OUTBOUND_TRANSPORT=http 27 | ACAPY_ADMIN=[0.0.0.0,3021] 28 | 29 | # Only used if run ./manage production 30 | # Typically you will want to update 0.0.0.0 to some public IP 31 | ACAPY_ENDPOINT=http://om-authority-agent:3020 32 | 33 | ACAPY_WEBHOOK_URL=http://om-authority-business-logic:3010 34 | ACAPY_ADMIN_API_KEY=adminApiKey 35 | ACAPY_LABEL=OpenMined 36 | ACAPY_WALLET_NAME=OM 37 | ACAPY_WALLET_KEY=omauthority_key 38 | ACAPY_WALLET_TYPE=indy 39 | 40 | #ACAPY_WALLET_STORAGE_TYPE=postgres_storage 41 | #ACAPY_WALLET_STORAGE_CONFIG={"url":"om-authority-wallet-db:5432","wallet_scheme":"MultiWalletSingleTable"} 42 | #ACAPY_WALLET_STORAGE_CREDS={"account":"postgres","password":"dbpass","admin_account":"postgres","admin_password":"dbpass"} 43 | ACAPY_AUTO_PROVISION=true 44 | 45 | 46 | ## Local Network 47 | # ACAPY_GENESIS_FILE=/home/indy/von-local-genesis-txns 48 | 49 | ## BC Gov Greenlight 50 | # ACAPY_GENESIS_URL=http://greenlight.bcovrin.vonx.io/genesis 51 | 52 | ## Sovrin BuilderNet 53 | ACAPY_GENESIS_URL=https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_builder_genesis 54 | 55 | ## Sovrin StagingNet 56 | # ACAPY_GENESIS_URL=https://raw.githubusercontent.com/sovrin-foundation/sovrin/master/sovrin/pool_transactions_sandbox_genesis 57 | 58 | 59 | # Multi-tenant Configuration 60 | # ACAPY_MULTITENANT=true 61 | # ACAPY_MULTITENANT_ADMIN=true 62 | # ACAPY_MULTITENANT_JWT_SECRET=jwtSecret 63 | 64 | # Name of ngrok container if exposing agent endpoint over ngrok 65 | NGROK_NAME= ngrok-om-authority 66 | 67 | 68 | # Optional Helper Configurations - See https://github.com/hyperledger/aries-cloudagent-python/blob/main/aries_cloudagent/config/argparse.py 69 | ACAPY_AUTO_ACCEPT_INVITES=true 70 | ACAPY_AUTO_ACCEPT_REQUESTS=true 71 | ACAPY_AUTO_PING_CONNECTION=true 72 | ACAPY_AUTO_RESPOND_MESSAGES=true 73 | ACAPY_AUTO_RESPOND_CREDENTIAL_PROPOSAL=true 74 | ACAPY_AUTO_RESPOND_CREDENTIAL_OFFER=true 75 | ACAPY_AUTO_RESPOND_CREDENTIAL_REQUEST=true 76 | ACAPY_AUTO_RESPOND_PRESENTATION_PROPOSAL=true 77 | ACAPY_AUTO_RESPOND_PRESENTATION_REQUEST=true 78 | ACAPY_AUTO_STORE_CREDENTIAL=true 79 | ACAPY_AUTO_VERIFY_PRESENTATION=true 80 | ACAPY_PRESERVE_EXCHANGE_RECORDS=true 81 | 82 | 83 | # Optional Trace Arguments 84 | # ACAPY_TRACE=true 85 | # ACAPY_TRACE_TARGET=log 86 | # ACAPY_TRACE_LABEL=Issuer 87 | 88 | # Timing arguments 89 | # ACAPY_TIMING=true 90 | # ACAPY_TIMING_LOG=/home/indy/logs/timing.txt 91 | 92 | # Debug Arguments 93 | # ACAPY_DEBUG=true 94 | 95 | ACAPY_LOG_LEVEL=debug 96 | # ACAPY_LOG_FILE=/home/indy/logs/agent_logs.txt -------------------------------------------------------------------------------- /tutorials/5. OM FoPC Course - Public Key Infrastructures/actors/om_authority/notebooks/issuer_agent_invite_QRcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenMined/PyDentity/1310244129d3fcbd8539a5e96adc306026b95fc8/tutorials/5. OM FoPC Course - Public Key Infrastructures/actors/om_authority/notebooks/issuer_agent_invite_QRcode.png -------------------------------------------------------------------------------- /tutorials/5. OM FoPC Course - Public Key Infrastructures/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | ngrok-dataowner: 4 | image: wernight/ngrok 5 | command: ngrok http dataowner-agent:3020 --log stdout 6 | env_file: 7 | - actors/dataowner/.env 8 | networks: 9 | - indy_demo 10 | dataowner-agent: 11 | build: 12 | context: ../../ 13 | dockerfile: dockerfiles/agents/Dockerfile.ngrok-new 14 | env_file: 15 | - actors/dataowner/.env 16 | ports: 17 | - 5020:3020 18 | - 5021:3021 19 | networks: 20 | - indy_demo 21 | dataowner-business-logic: 22 | build: 23 | context: ../../ 24 | dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller 25 | env_file: 26 | - actors/dataowner/.env 27 | depends_on: 28 | - dataowner-agent 29 | networks: 30 | - indy_demo 31 | volumes: 32 | - ${PWD}/actors/dataowner/notebooks:/workspace 33 | ports: 34 | - "8889:8888" 35 | - 5010:3010 36 | om-authority-wallet-db: 37 | image: postgres:11 38 | container_name: om-authority-wallet-db 39 | command: postgres -c listen_addresses='*' 40 | env_file: 41 | - actors/om_authority/.env 42 | volumes: 43 | - ./resources/init.sql:/docker-entrypoint-initdb.d/init.sql 44 | networks: 45 | - indy_demo 46 | tty: true 47 | ngrok-om-authority: 48 | image: wernight/ngrok 49 | command: ngrok http om-authority-agent:3020 --log stdout 50 | env_file: 51 | - actors/om_authority/.env 52 | networks: 53 | - indy_demo 54 | om-authority-agent: 55 | build: 56 | context: ../../ 57 | dockerfile: dockerfiles/agents/Dockerfile.ngrok-new 58 | env_file: 59 | - actors/om_authority/.env 60 | ports: 61 | - 3020:3020 62 | - 3021:3021 63 | depends_on: 64 | - om-authority-wallet-db 65 | networks: 66 | - indy_demo 67 | om-authority-business-logic: 68 | build: 69 | context: ../../ 70 | dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller 71 | env_file: 72 | - actors/om_authority/.env 73 | depends_on: 74 | - om-authority-agent 75 | networks: 76 | - indy_demo 77 | volumes: 78 | - ./actors/om_authority/notebooks:/workspace 79 | ports: 80 | - "8888:8888" 81 | - 3010:3010 82 | ngrok-datascientist: 83 | image: wernight/ngrok 84 | command: ngrok http datascientist-agent:3020 --log stdout 85 | env_file: 86 | - actors/datascientist/.env 87 | networks: 88 | - indy_demo 89 | datascientist-agent: 90 | build: 91 | context: ../../ 92 | dockerfile: dockerfiles/agents/Dockerfile.ngrok-new 93 | env_file: 94 | - actors/datascientist/.env 95 | ports: 96 | # HTTP_PORT 97 | - 4020:3020 98 | # ADMIN_PORT 99 | - 4021:3021 100 | networks: 101 | - indy_demo 102 | datascientist-business-logic: 103 | build: 104 | context: ../../ 105 | dockerfile: dockerfiles/controllers/Dockerfile.basiccontroller 106 | env_file: 107 | - actors/datascientist/.env 108 | depends_on: 109 | - datascientist-agent 110 | networks: 111 | - indy_demo 112 | volumes: 113 | - ./actors/datascientist/notebooks:/workspace 114 | ports: 115 | - "8890:8888" 116 | - 4010:3010 117 | 118 | networks: 119 | indy_demo: -------------------------------------------------------------------------------- /tutorials/5. OM FoPC Course - Public Key Infrastructures/resources/init.sql: -------------------------------------------------------------------------------- 1 | -- We could also handle this the with storage plugin itself, but sense this only needs to get run once the very first time using separate sql statements 2 | CREATE TABLE IF NOT EXISTS metadata ( 3 | wallet_id VARCHAR(64) NOT NULL, 4 | value BYTEA NOT NULL, 5 | PRIMARY KEY(wallet_id) 6 | ); 7 | CREATE UNIQUE INDEX IF NOT EXISTS ux_metadata_values ON metadata(wallet_id, value); 8 | CREATE UNIQUE INDEX IF NOT EXISTS ux_metadata_wallet_id ON metadata(wallet_id); 9 | 10 | CREATE TABLE IF NOT EXISTS items( 11 | wallet_id VARCHAR(64) NOT NULL, 12 | id BIGSERIAL NOT NULL, 13 | type BYTEA NOT NULL, 14 | name BYTEA NOT NULL, 15 | value BYTEA NOT NULL, 16 | key BYTEA NOT NULL, 17 | PRIMARY KEY(wallet_id, id) 18 | ); 19 | CREATE UNIQUE INDEX IF NOT EXISTS ux_items_wallet_id_id ON items(wallet_id, id); 20 | CREATE UNIQUE INDEX IF NOT EXISTS ux_items_type_name ON items(wallet_id, type, name); 21 | 22 | CREATE TABLE IF NOT EXISTS tags_encrypted( 23 | wallet_id VARCHAR(64) NOT NULL, 24 | name BYTEA NOT NULL, 25 | value BYTEA NOT NULL, 26 | item_id BIGINT NOT NULL, 27 | PRIMARY KEY(wallet_id, name, item_id), 28 | FOREIGN KEY(wallet_id, item_id) 29 | REFERENCES items(wallet_id, id) 30 | ON DELETE CASCADE 31 | ON UPDATE CASCADE 32 | ); 33 | CREATE INDEX IF NOT EXISTS ix_tags_encrypted_name ON tags_encrypted(wallet_id, name); 34 | CREATE INDEX IF NOT EXISTS ix_tags_encrypted_value ON tags_encrypted(wallet_id, sha256(value)); 35 | CREATE INDEX IF NOT EXISTS ix_tags_encrypted_wallet_id_item_id ON tags_encrypted(wallet_id, item_id); 36 | 37 | CREATE TABLE IF NOT EXISTS tags_plaintext( 38 | wallet_id VARCHAR(64) NOT NULL, 39 | name BYTEA NOT NULL, 40 | value TEXT NOT NULL, 41 | item_id BIGINT NOT NULL, 42 | PRIMARY KEY(wallet_id, name, item_id), 43 | FOREIGN KEY(wallet_id, item_id) 44 | REFERENCES items(wallet_id, id) 45 | ON DELETE CASCADE 46 | ON UPDATE CASCADE 47 | ); 48 | CREATE INDEX IF NOT EXISTS ix_tags_plaintext_name ON tags_plaintext(wallet_id, name); 49 | CREATE INDEX IF NOT EXISTS ix_tags_plaintext_value ON tags_plaintext(wallet_id, value); 50 | CREATE INDEX IF NOT EXISTS ix_tags_plaintext_wallet_id_item_id ON tags_plaintext(wallet_id, item_id); --------------------------------------------------------------------------------