diff --git a/.codecov.yaml b/.codecov.yaml new file mode 100644 index 0000000..e2065a2 --- /dev/null +++ b/.codecov.yaml @@ -0,0 +1,17 @@ +coverage: + status: + project: + default: + target: 85 + paths: ["src"] + patch: + default: + target: 75 + paths: ["src"] + +ignore: + - "setup.py" + - "integration" + - "tests" + - "scripts" + - "src/signify/app/cli/*" diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..616d7dd --- /dev/null +++ b/.coveragerc @@ -0,0 +1,8 @@ +[run] +omit = + scripts/* + integration/* + docs/* + tests/* + setup.py + src/signify/app/cli/* \ No newline at end of file diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..5b48406 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,32 @@ +name: Tests +on: + push: + branches: + - 'main' + - 'development' + pull_request: + workflow_dispatch: + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ macos-latest, ubuntu-latest ] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10.4 + uses: actions/setup-python@v2 + with: + python-version: 3.10.4 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest mockito pytest-cov codecov + pip install -r requirements.txt + - name: Run tests + run: | + pytest --cov=./ --cov-report=xml + codecov diff --git a/.gitignore b/.gitignore index a3c37b9..ba3ab77 100644 --- a/.gitignore +++ b/.gitignore @@ -130,4 +130,7 @@ dmypy.json # Intellij specific -.idea \ No newline at end of file +.idea + +# vscode specific +.vscode \ No newline at end of file diff --git a/README.md b/README.md index e7a33e4..869a858 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ -# signifypy +# Signifypy Signify implementation in Python + +[![Tests](https://github.com/WebOfTrust/signifypy/actions/workflows/test.yaml/badge.svg?branch=development)](https://github.com/WebOfTrust/signifypy/actions/workflows/test.yaml) +[![codecov](https://codecov.io/gh/WebOfTrust/signifypy/graph/badge.svg?token=E9VS4PNKTD)](https://codecov.io/gh/WebOfTrust/signifypy) +[![Documentation Status](https://readthedocs.org/projects/signifypy/badge/?version=latest)](https://signifypy.readthedocs.io/en/latest/?badge=latest) + +## Signify - KERI Signing at the Edge + +Of the five functions in a KERI agent, + +1. Key generation +2. Encrypted key storage +3. Event generation +4. Event signing +5. Event Validation + +Signifypy provides key generation and event signing in a library to provide "signing at the edge". +It accomplishes this by using [libsodium](https://doc.libsodium.org/) to generate ed25519 key pairs for signing and x25519 key pairs for encrypting the +private keys, next public keys, and salts used to generate the private keys. The encrypted private key and salts are then stored on a +remote cloud agent that never has access to the decryption keys. New key pair sets (current and next) will be generated +for inception and rotation events with only the public keys and blake3 hash of the next keys made available to the agent. + +The communication protocol between a Signify client and [KERI](https://github.com/WebOfTrust/keri) agent will encode all cryptographic primitives as CESR base64 +encoded strings for the initial implementation. Support for binary CESR can be added in the future. + +### Development + +``` +pip install pytest mockito +``` + +`pytest` to run tests. diff --git a/integration/__init__.py b/integration/__init__.py deleted file mode 100644 index 6305a07..0000000 --- a/integration/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -integration test package - - -""" diff --git a/integration/app/integration.sh b/integration/app/integration.sh deleted file mode 100755 index e2de866..0000000 --- a/integration/app/integration.sh +++ /dev/null @@ -1,382 +0,0 @@ -#!/bin/bash - -# -# Run this script from the base SignifyPy directory, like -# signifypy% ./integration/app/delegate.sh -# - -#print commands -#set -x - -#save this current directory, this is where the integration_clienting file also is -ORIG_CUR_DIR=$( pwd ) - -KERI_PRIMARY_STORAGE="/usr/local/var/keri" -KERI_FALLBACK_STORAGE="${HOME}/.keri" - -KERI_DEV_BRANCH="development" -KERI_DEV_TAG="c3a6fc455b5fac194aa9c264e48ea2c52328d4c5" -VLEI_DEV_BRANCH="dev" -VLEI_DEV_TAG="ed982313dab86bfada3825857601a10d71ce9631" -KERIA_DEV_BRANCH="main" -KERIA_DEV_TAG="65bebb4912557067ca290f4765e85aafa657c46f" -SIGNIFY_DEV_BRANCH="main" - -prompt="y" -function intro() { - echo "Welcome to the integration test setup/run/teardown script" - read -p "Enable prompts?, [y]: " enablePrompts - prompt=${enablePrompts:-"y"} - if [ "${prompt}" != "n" ]; then - echo "Prompts enabled" - else - echo "Skipping prompts, using defaults" - fi -} - -function getKeripyDir() { - # Check if the environment variable is set - if [ -z "$KERIPY_DIR" ]; then - default_value="../keripy" - # Prompt the user for input with a default value - if [ "${prompt}" == "y" ]; then - read -p "Set keripy dir [${default_value}]: " keriDirInput - fi - # Set the value to the user input or the default value - KERIPY_DIR=${keriDirInput:-$default_value} - fi - # Use the value of the environment variable - echo "$KERIPY_DIR" -} - -function getVleiDir() { - # Check if the environment variable is set - if [ -z "$VLEI_DIR" ]; then - default_value="../vLEI" - # Prompt the user for input with a default value - if [ "${prompt}" == "y" ]; then - read -p "Set vlei dir [${default_value}]: " vleiDirInput - fi - # Set the value to the user input or the default value - VLEI_DIR=${vleiDirInput:-$default_value} - fi - # Use the value of the environment variable - echo "$VLEI_DIR" -} - -function getKeriaDir() { - # Check if the environment variable is set - if [ -z "$KERIA_DIR" ]; then - default_value="../keria" - # Prompt the user for input with a default value - if [ "${prompt}" == "y" ]; then - read -p "Set keria dir [${default_value}]: " keriaDirInput - fi - # Set the value to the user input or the default value - KERIA_DIR=${keriaDirInput:-$default_value} - fi - # Use the value of the environment variable - echo "$KERIA_DIR" -} - -function runDelegator() { - #create the delegator from keripy - keriDir=$1 - echo "Creating delegator" - KERIPY_SCRIPTS_DIR="${keriDir}/scripts" - delPid=-1 - if [ -d "${KERIPY_SCRIPTS_DIR}" ]; then - kli init --name delegator --nopasscode --config-dir "${KERIPY_SCRIPTS_DIR}" --config-file demo-witness-oobis --salt 0ACDEyMzQ1Njc4OWdoaWpsaw - KERIPY_DELEGATOR_CONF="${KERIPY_SCRIPTS_DIR}/demo/data/delegator.json" - if [ -f "${KERIPY_DELEGATOR_CONF}" ]; then - kli incept --name delegator --alias delegator --file "${KERIPY_DELEGATOR_CONF}" - # kli incept --name delegator --alias delegator --file /Users/meenyleeny/VSCode/keripy/scripts/demo/data/delegator.json - echo "Delegator created" - # delgator auto-accepts the delegation request - kli delegate confirm --name delegator --alias delegator -Y & - delPid=$! - echo "Delegator waiting to auto-accept delegation request" - else - echo "Delegator configuration missing ${KERIPY_DELEGATOR_CONF}" - fi - else - echo "KERIPY directory ${KERIPY_SCRIPTS_DIR} does not exist." - fi -} - -function runMultisig() { - #create the delegator from keripy - keriDir=$1 - echo "Creating multisig" - KERIPY_SCRIPTS_DIR="${keriDir}/scripts" - delPid=-1 - if [ -d "${KERIPY_SCRIPTS_DIR}" ]; then - - # Follow commands run in parallel - kli multisig incept --name multisig1 --alias multisig1 --group multisig --file ${KERI_DEMO_SCRIPT_DIR}/data/multisig-triple-sample.json & - pid=$! - PID_LIST+=" $pid" - kli multisig incept --name multisig2 --alias multisig2 --group multisig --file ${KERI_SCRIPTS_DIR}/data/multisig-triple-sample.json & - pid=$! - PID_LIST+=" $pid" - - - kli init --name multisig1 --salt 0ACDEyMzQ1Njc4OWxtbm9aBc --nopasscode --config-dir "${KERIPY_SCRIPTS_DIR}" --config-file demo-witness-oobis - kli init --name multisig2 --salt 0ACDEyMzQ1Njc4OWdoaWpsaw --nopasscode --config-dir "${KERIPY_SCRIPTS_DIR}" --config-file demo-witness-oobis - KERIPY_MULTISIG_CONF_1="${KERIPY_SCRIPTS_DIR}/demo/data/multisig-1-sample.json" - KERIPY_MULTISIG_CONF_2="${KERIPY_SCRIPTS_DIR}/demo/data/multisig-2-sample.json" - if [ -f "${KERIPY_MULTISIG_CONF_2}" ]; then - kli incept --name multisig1 --alias multisig1 --file "${KERIPY_MULTISIG_CONF_1}" - kli incept --name multisig2 --alias multisig2 --file "${KERIPY_MULTISIG_CONF_2}" - - echo "Multisig 1 and 2 created" - kli oobi resolve --name multisig1 --oobi-alias multisig2 --oobi http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness/BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha & - kli oobi resolve --name multisig1 --oobi-alias multisig3 --oobi http://127.0.0.1:5642/oobi/EKzS2BGQ7qkmEfsjGdx2w5KwmpWKf7lEXAMfB4AKqvUe/witness/BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha & - kli oobi resolve --name multisig2 --oobi-alias multisig1 --oobi http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness/BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha & - kli oobi resolve --name multisig2 --oobi-alias multisig3 --oobi http://127.0.0.1:5642/oobi/EKzS2BGQ7qkmEfsjGdx2w5KwmpWKf7lEXAMfB4AKqvUe/witness/BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha & - echo "All participants of multisig looking for each other" - else - echo "Multisig configuration missing ${KERIPY_MULTISIG_CONF_2}" - fi - else - echo "KERIPY directory ${KERIPY_SCRIPTS_DIR} does not exist." - fi -} - -function runIssueEcr() { - cd "${ORIG_CUR_DIR}" || exit - if [ "${prompt}" == "y" ]; then - read -p "Run vLEI issue ECR script (n to skip)?, [n]: " runEcr - fi - runIssueEcr=${runEcr:-"n"} - if [ "${runIssueEcr}" == "n" ]; then - echo "Skipping Issue ECR script" - else - echo "Running issue ECR script" - scriptsDir="scripts" - if [ -d "${scriptsDir}" ]; then - echo "Launching Issue ECR script" - cd ${scriptsDir} || exit - source env.sh - source issue-ecr.sh - echo "Completed issue ECR script" - python list_person_credentials.py - echo "Listed person credentials" - fi - fi - cd "${ORIG_CUR_DIR}" || exit -} - -function runKeri() { - cd ${ORIG_CUR_DIR} || exit - witPid=-1 - keriDir=$(getKeripyDir) - echo "Keripy dir set to: ${keriDir}" - if [ "${prompt}" == "y" ]; then - read -p "Run witness network (y/n)? [y]: " runWitNet - fi - runWit=${runWitNet:-"y"} - if [ "${runWit}" == "y" ]; then - if [ -d "${keriDir}" ]; then - #run a clean witness network - echo "Launching a clean witness network" - cd "${keriDir}" || exit - updateFromGit ${KERI_DEV_BRANCH} - installPythonUpdates "keri" - rm -rf ${KERI_PRIMARY_STORAGE}/*;rm -Rf ${KERI_FALLBACK_STORAGE}/*;kli witness demo & - witPid=$! - sleep 5 - echo "Clean witness network launched" - else - echo "KERIPY dir missing ${keriDir}" - exit 1 - fi - else - echo "Skipping witness network" - fi - echo "" -} - -function runKeria() { - # run keria cloud agent - keriaPid=-1 - - if [ "${prompt}" == "y" ]; then - read -p "Run Keria (y/n)? [y]: " runKeriaInput - fi - runKeria=${runKeriaInput:-"y"} - if [ "${runKeria}" == "y" ]; then - echo "Running keria cloud agent" - keriaDir=$(getKeriaDir) - if [ -d "${keriaDir}" ]; then - cd "${keriaDir}" || exit - updateFromGit ${KERIA_DEV_BRANCH} - installPythonUpdates "keria" - export KERI_AGENT_CORS=true - keria start --config-file demo-witness-oobis.json --config-dir "${keriaDir}/scripts" & - keriaPid=$! - sleep 5 - echo "Keria cloud agent running" - else - echo "Keria dir missing ${keriaDir}" - fi - fi - echo "" -} - -function runSignifyIntegrationTests() { - # Assumes you are running from the base signify dir (see hints at the top) - cd "${ORIG_CUR_DIR}" || exit - integrationTestModule="integration.app.integration_clienting" - echo "Available functions in ${integrationTestModule}" - testList=$(python -c "import ${integrationTestModule}; print('\n'.join(x for x in dir(${integrationTestModule}) if x.startswith('test_')))") - echo "${testList}" - # echo "all" - - read -p "What signify test to run (n to skip integration tests)?, [${runSignify}]: " runSigInput - runSignify=${runSigInput:-"n"} - if [ "${runSignify}" == "n" ]; then - echo "Skipping signify test" - else - runIntegrationTest "${runSignify}" - fi -} - -function runIntegrationTest() { - testName=$1 - echo "Launching Signifypy integration test ${testName}" - signifyPid=-1 - updateFromGit ${SIGNIFY_DEV_BRANCH} - installPythonUpdates "signify" - iClient="./integration/app/integration_clienting.py" - if [ -f "${iClient}" ]; then - if [ "${testName}" == "test_delegation" ]; then - runDelegator "${keriDir}" - fi - if [ "${testName}" == "test_multisig" ]; then - runMultisig "${keriDir}" - fi - python -c "from ${integrationTestModule} import ${testName}; ${testName}()" & - signifyPid=$! - sleep 10 - echo "Completed signify ${testName}" - else - echo "${iClient} module missing" - exit 1 - fi -} - -function runVlei() { - # run vLEI cloud agent - cd ${ORIG_CUR_DIR} || exit - vleiPid=-1 - if [ "${prompt}" == "y" ]; then - read -p "Run vLEI (y/n)? [y]: " runVleiInput - fi - runVlei=${runVleiInput:-"y"} - if [ "${runVlei}" == "y" ]; then - echo "Running vLEI server" - vleiDir=$(getVleiDir) - if [ -d "${vleiDir}" ]; then - cd "${vleiDir}" || exit - updateFromGit ${VLEI_DEV_BRANCH} - installPythonUpdates "vlei" - vLEI-server -s ./schema/acdc -c ./samples/acdc/ -o ./samples/oobis/ & - vleiPid=$! - sleep 5 - echo "vLEI server is running" - else - echo "vLEI dir missing ${vleiDir}" - fi - fi - echo "" -} - -function installPythonUpdates() { - name=$1 - if [ "${prompt}" == "y" ]; then - read -p "Install $name?, [n]: " installInput - fi - install=${installInput:-"n"} - if [ "${install}" == "n" ]; then - echo "Skipping install of $name" - else - echo "Installing python module updates..." - python -m pip install -e . - fi -} - -function updateFromGit() { - branch=$1 - commit=$2 - - if [ "${prompt}" == "y" ]; then - read -p "Update git repo ${branch} ${commit}?, [n]: " upGitInput - fi - update=${upGitInput:-"n"} - if [ "${update}" == "y" ]; then - echo "Updating git branch ${branch} ${commit}" - fetch=$(git fetch) - echo "git fetch status ${fetch}" - if [ -z "${commit}" ]; then - switch=$(git switch "${branch}") - echo "git switch status ${switch}" - pull=$(git pull) - echo "git pull status ${pull}" - else - switch=$(git checkout "${commit}") - echo "git checkout commit status ${switch}" - fi - else - echo "Skipping git update ${branch}" - fi -} - -runInt="test_salty" -while [ "${runInt}" != "n" ] -do - intro - - echo "Setting up..." - - runKeri - - sleep 3 - - runVlei - - sleep 3 - - runKeria - - sleep 3 - - runSignifyIntegrationTests - - sleep 3 - - runIssueEcr - - echo "" - - if [ "${prompt}" == "y" ]; then - read -p "Your servers still running, hit enter to tear down: " teardown - fi - - echo "Tearing down any leftover processes" - #tear down the signify client - kill "$signifyPid" >/dev/null 2>&1 - # tear down the keria cloud agent - kill $keriaPid >/dev/null 2>&1 - # tear down the delegator - kill "$delPid" >/dev/null 2>&1 - # tear down the vLEI server - kill $vleiPid >/dev/null 2>&1 - # tear down the witness network - kill $witPid >/dev/null 2>&1 - - read -p "Run another test [n]?: " runAgain - runInt=${runAgain:-"n"} -done - -echo "Done" \ No newline at end of file diff --git a/integration/app/integration_clienting.py b/integration/app/integration_clienting.py deleted file mode 100644 index bcea1de..0000000 --- a/integration/app/integration_clienting.py +++ /dev/null @@ -1,934 +0,0 @@ -# -*- encoding: utf-8 -*- -""" -SIGNIFY -signify.app.clienting module - -Testing clienting with integration tests that require a running KERIA Cloud Agent -""" -import json -import random -from time import sleep - -import requests -from keri.app.keeping import Algos -from keri.core import coring -from responses import _recorder - -import pytest -from keri import kering -from keri.core.coring import Tiers, Serder, MtrDex - -import keria.app.aiding as aiding - -from signify.app.clienting import SignifyClient - -TESTS_APP_DIR = "tests/app/" -WITNESS_FILE_PATH = "{}{}".format(TESTS_APP_DIR,"witness.toml") -DELEGATION_FILE_PATH = "{}{}".format(TESTS_APP_DIR,"delegation.toml") -CONNECT_FILE_PATH = "{}{}".format(TESTS_APP_DIR,"connect.toml") - -wit1 = "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha" -wit2 = "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM" -wit3 = "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" - -def test_init(): - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = None - - # Try with bran that is too short - with pytest.raises(kering.ConfigurationError): - SignifyClient(passcode=bran[:16], tier=tier) - - # Try with an invalid URL - with pytest.raises(kering.ConfigurationError): - client = SignifyClient(passcode=bran, tier=tier) - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url="ftp://www.example.com") - - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - serder = client.icp - assert serder.raw == (b'{"v":"KERI10JSON00012b_","t":"icp","d":"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJ' - b'XJHtJose","i":"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose","s":"0","kt":"1' - b'","k":["DAbWjobbaLqRB94KiAutAHb_qzPpOHm3LURA_ksxetVc"],"nt":"1","n":["EIFG_u' - b'qfr1yN560LoHYHfvPAhxQ5sN6xZZT_E3h7d2tL"],"bt":"0","b":[],"c":[],"a":[]}') - - # changing tier with has no effect - tier = Tiers.low - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - tier = Tiers.med - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "EOgQvKz8ziRn7FdR_ebwK9BkaVOnGeXQOJ87N6hMLrK0" - - tier = Tiers.high - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "EB8wN2c_tv1WlsJ5c3949-TFWPMB2IflFbdMlZfC_Hgo" - - -def test_extern(): - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = None - - client = SignifyClient(passcode=bran, tier=tier, - extern_modules=[ - dict( - type="gcp", - name="gcp_ksm_shim", - params=dict( - projectId="advance-copilot-319717", - locationId="us-west1", - keyRingId="signify-key-ring" - ) - )]) - - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" - assert client.ctrl.ridx == 0 - - # Create AID using external HSM module - stem = "ABO4qF9g9L-e1QzvMXgY-58elMh8L-63ZBnNXhxScO81" - identifiers = client.identifiers() - aid = identifiers.create("aid1", algo=Algos.extern, extern_type="gcp", extern=dict(stem=stem)) - icp = Serder(ked=aid) - print(icp.pretty()) - - -@_recorder.record(file_path=CONNECT_FILE_PATH) -def test_salty(): - """ - This test assumes a running KERIA agent. - See integration.sh to run this test automatically with a locally running KERIA and witness network. - """ - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.med - - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "EOgQvKz8ziRn7FdR_ebwK9BkaVOnGeXQOJ87N6hMLrK0" - - # Raises configuration error because the started agent has a different controller AID - with pytest.raises(kering.ConfigurationError): - client.connect(url=url) - - tier = Tiers.low - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - - print(evt.pretty()) - print(siger.qb64) - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - identifiers = client.identifiers() - aids = identifiers.list() - assert aids == [] - - op = identifiers.create("aid1", bran="0123456789abcdefghijk") - aid = op["response"] - icp = Serder(ked=aid) - assert icp.pre == "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK" - assert len(icp.verfers) == 1 - assert icp.verfers[0].qb64 == "DPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9" - assert len(icp.digers) == 1 - assert icp.digers[0].qb64 == "EAORnRtObOgNiOlMolji-KijC_isa3lRDpHCsol79cOc" - assert icp.tholder.num == 1 - assert icp.ntholder.num == 1 - - rpy = identifiers.makeEndRole(pre=icp.pre, eid="EPGaq6inGxOx-VVVEcUb_KstzJZldHJvVsHqD4IPxTWf") - print(rpy.pretty()) - assert rpy.ked['a']['cid'] == "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK" - assert rpy.ked['a']['eid'] == "EPGaq6inGxOx-VVVEcUb_KstzJZldHJvVsHqD4IPxTWf" - - aids = identifiers.list() - assert len(aids) == 1 - aid = aids.pop() - - salt = aid[Algos.salty] - assert aid['name'] == "aid1" - assert salt["pidx"] == 0 - assert aid["prefix"] == icp.pre - assert salt["stem"] == "signify:aid" - - op2 = identifiers.create("aid2", count=3, ncount=3, isith="2", nsith="2", bran="0123456789lmnopqrstuv") - aid2 = op2["response"] - icp2 = Serder(ked=aid2) - print(icp2.pre) - assert icp2.pre == "EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX" - assert len(icp2.verfers) == 3 - assert icp2.verfers[0].qb64 == "DGBw7C7AfC7jbD3jLLRS3SzIWFndM947TyNWKQ52iQx5" - assert icp2.verfers[1].qb64 == "DD_bHYFsgWXuCbz3SD0HjCIe_ITjRvEoCGuZ4PcNFFDz" - assert icp2.verfers[2].qb64 == "DEe9u8k0fm1wMFAuOIsCtCNrpduoaV5R21rAcJl0awze" - assert len(icp2.digers) == 3 - print([diger.qb64 for diger in icp2.digers]) - assert icp2.digers[0].qb64 == "EML5FrjCpz8SEl4dh0U15l8bMRhV_O5iDcR1opLJGBSH" - assert icp2.digers[1].qb64 == "EJpKquuibYTqpwMDqEFAFs0gwq0PASAHZ_iDmSF3I2Vg" - assert icp2.digers[2].qb64 == "ELplTAiEKdobFhlf-dh1vUb2iVDW0dYOSzs1dR7fQo60" - assert icp2.tholder.num == 2 - assert icp2.ntholder.num == 2 - - aids = identifiers.list() - assert len(aids) == 2 - aid = aids[1] - assert aid['name'] == "aid2" - assert aid["prefix"] == icp2.pre - salt = aid[Algos.salty] - assert salt["pidx"] == 1 - assert salt["stem"] == "signify:aid" - - op3 = identifiers.rotate("aid1") - ked = op3["response"] - rot = Serder(ked=ked) - - assert rot.said == "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg" - assert rot.sn == 1 - assert len(rot.digers) == 1 - assert rot.verfers[0].qb64 == "DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly" - assert rot.digers[0].qb64 == "EJMovBlrBuD6BVeUsGSxLjczbLEbZU9YnTSud9K4nVzk" - - op4 = identifiers.interact("aid1", data=[icp.pre]) - ked = op4["response"] - ixn = Serder(ked=ked) - assert ixn.said == "ENsmRAg_oM7Hl1S-GTRMA7s4y760lQMjzl0aqOQ2iTce" - assert ixn.sn == 2 - assert ixn.ked["a"] == [icp.pre] - - aid = identifiers.get("aid1") - state = aid["state"] - assert state['s'] == '2' - assert state['f'] == '2' - assert state['et'] == 'ixn' - assert state['d'] == ixn.said - assert state['ee']['d'] == rot.said - - events = client.keyEvents() - log = events.get(pre=aid["prefix"]) - assert len(log) == 3 - serder = coring.Serder(ked=log[0]) - assert serder.pre == icp.pre - assert serder.said == icp.said - serder = coring.Serder(ked=log[1]) - assert serder.pre == rot.pre - assert serder.said == rot.said - serder = coring.Serder(ked=log[2]) - assert serder.pre == ixn.pre - assert serder.said == ixn.said - - print(identifiers.list()) - - -@_recorder.record(file_path=WITNESS_FILE_PATH) -def test_witnesses(): - """ This test assumes a running Demo Witnesses and KERIA agent with the following comands: - - `kli witness demo` - `keria start -c ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose \ - --config-file demo-witness-oobis --config-dir /scripts` - - """ - import os - dir_path = os.path.dirname(os.path.realpath(__file__)) - print("current path: ", dir_path) - print("should find witenss toml file here: ",os.listdir("{}/../../{}".format(dir_path,TESTS_APP_DIR))) - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == 'EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei' - - # assert client.ctrl.ridx == 0 - - identifiers = client.identifiers() - operations = client.operations() - - print("creating aid") - # Use witnesses - op = identifiers.create("aid1", bran="canIGetAWitnessSaltGreaterThan21", toad="2", wits=["BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"]) - - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - icp1 = Serder(ked=op["response"]) - assert icp1.pre == 'EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa' - - - assert icp1.ked['b'] == ["BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"] - assert icp1.ked['bt'] == "2" - - aid1 = identifiers.get("aid1") - assert aid1["prefix"] == icp1.pre - assert len(aid1["windexes"]) == 3 - - aids = identifiers.list() - assert len(aids) == 1 - aid = aids.pop() - assert aid['prefix'] == icp1.pre - - -@_recorder.record(file_path=DELEGATION_FILE_PATH) -def test_delegation(): - """ This test assumes a running Demo Witnesses and KERIA agent with the following comands: - - `kli witness demo` - `keria start \ - --config-file demo-witness-oobis --config-dir /scripts` - - """ - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - - client = SignifyClient(passcode=bran, tier=tier) - print(client.controller) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - # Delegator OOBI: - # http://127.0.0.1:5642/oobi/EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7/witness - - delpre = "EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7" - identifiers = client.identifiers() - operations = client.operations() - oobis = client.oobis() - - op = oobis.resolve(f"http://127.0.0.1:5642/oobi/{delpre}/witness") - print("OOBI op is: ", op) - - count = 0 - while not op["done"] and not count > 25: - op = operations.get(op["name"]) - sleep(1) - - op = identifiers.create("aid1", toad="2", delpre=delpre, wits=[wit1, wit2, wit3]) - pre = op["metadata"]["pre"] - - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - icp1 = Serder(ked=op["response"]) - - print(icp1.pretty()) - assert icp1.pre == pre - - -def test_multisig(): - """ This test assumes a running Demo Witnesses and KERIA agent with the following comands: - - `kli witness demo` - `keria start --config-file demo-witness-oobis --config-dir /scripts` - - """ - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - - client = SignifyClient(passcode=bran, tier=tier) - print(client.controller) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" - # assert client.ctrl.ridx == 0 - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - identifiers = client.identifiers() - operations = client.operations() - oobis = client.oobis() - - op = identifiers.create("multisig3", bran="0123456789lmnopqrstuv") - icp = op["response"] - serder = coring.Serder(ked=icp) - assert serder.pre == "EOGvmhJDBbJP4zeXaRun5vSz0O3_1zB10DwNMyjXlJEv" - print(f"created AID {serder.pre}") - - identifiers.addEndRole("multisig3", eid=client.agent.pre) - - print(f"OOBI for {serder.pre}:") - oobi = oobis.get("multisig3") - print(oobi) - - op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness/BBilc4" - "-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - alias="multisig1") - - print("resolving oobi for multisig1") - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - multisig1 = op["response"] - print("resolving oobi for multisig2") - op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness/BBilc4" - "-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - alias="multisig2") - - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - multisig2 = op["response"] - - m3 = identifiers.get("multisig3") - agent0 = m3["state"] - print(f"agent is {agent0}") - - states = rstates = [multisig2, multisig1, agent0] - - op = identifiers.create("multisig", algo=Algos.group, mhab=m3, - isith=["1/3", "1/3", "1/3"], nsith=["1/3", "1/3", "1/3"], - toad=3, - wits=[ - "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" - ], - states=states, - rstates=rstates) - print("waiting on multisig creation...") - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - gAid = op["response"] - print(f"group multisig created {gAid}") - - # Join an interaction event with the group - data = {"i": "EE77q3_zWb5ojgJr-R1vzsL5yiL4Nzm-bfSOQzQl02dy"} - op = identifiers.interact("multisig", data=data) - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - ixn = coring.Serder(ked=op["response"]) - events = client.keyEvents() - log = events.get(pre=ixn.pre) - assert len(log) == 2 - - for event in log: - print(coring.Serder(ked=event).pretty()) - - op2 = identifiers.rotate("multisig3") - rot = op2["response"] - serder = coring.Serder(ked=rot) - print(f"rotated multisig3 to {serder.sn}") - - aid1 = identifiers.get("multisig3") - agent0 = aid1["state"] - keyState = client.keyStates() - op = keyState.query(pre="EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4", sn=1) - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - multisig1 = op["response"] - print(f"using key {multisig1['k'][0]}") - print(f"using dig {multisig1['n'][0]}") - - op = keyState.query(pre="EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1", sn=1) - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - multisig2 = op["response"] - print(f"using key {multisig2['k'][0]}") - print(f"using dig {multisig2['n'][0]}") - - states = rstates = [multisig1, multisig2, agent0] - - op = identifiers.rotate("multisig", states=states, rstates=rstates) - print(op) - - -def test_randy(): - """ This test assumes a running KERIA agent with the following comand: - - `keria start -c ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose` - - """ - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" - # assert client.ctrl.ridx == 0 - - identifiers = client.identifiers() - op = identifiers.create("aid1", algo=Algos.randy) - aid = op["response"] - icp = Serder(ked=aid) - assert len(icp.verfers) == 1 - assert len(icp.verfers) == 1 - assert len(icp.digers) == 1 - assert len(icp.digers) == 1 - assert icp.tholder.num == 1 - assert icp.ntholder.num == 1 - - aids = identifiers.list() - assert len(aids) == 1 - aid = aids[0] - assert aid["prefix"] == icp.pre - - op = identifiers.interact("aid1", data=[icp.pre]) - ked = op["response"] - ixn = Serder(ked=ked) - assert ixn.sn == 1 - assert ixn.ked["a"] == [icp.pre] - - aids = identifiers.list() - assert len(aids) == 1 - aid = aids[0] - events = client.keyEvents() - log = events.get(pre=aid["prefix"]) - assert len(log) == 2 - - op = identifiers.rotate("aid1") - ked = op["response"] - rot = Serder(ked=ked) - assert rot.pre == icp.pre - assert rot.sn == 2 - assert len(rot.digers) == 1 - assert rot.verfers[0].qb64 != icp.verfers[0].qb64 - assert rot.digers[0].qb64 != icp.digers[0].qb64 - dig = coring.Diger(ser=rot.verfers[0].qb64b, code=MtrDex.Blake3_256) - print(icp.digers[0].qb64, dig.qb64) - assert icp.digers[0].qb64 == dig.qb64 - log = events.get(pre=aid["prefix"]) - assert len(log) == 3 - - for event in log: - print(coring.Serder(ked=event).pretty()) - - -def test_query(): - """ This test assumes a running Demo Witnesses and KERIA agent with the following comands: - - `kli witness demo` - `keria start -c ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose \ - --config-file demo-witness-oobis --config-dir /scripts` - - """ - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" - # assert client.ctrl.ridx == 0 - - operations = client.operations() - keyState = client.keyStates() - op = keyState.query(pre="EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4") - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - multisig1 = op["response"] - - op = keyState.query(pre="EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1") - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - multisig2 = op["response"] - - -def test_multi_tenant(): - """ This test assumes a running KERIA agent with the following comand: - - `keria start -c ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose` - - """ - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" - - identifiers = client.identifiers() - op = identifiers.create("aid1") - aid = op["response"] - icp = Serder(ked=aid) - - # assert icp.pre == "EKGpuLBMAncuGm2mk4F7tEtELqLEl60tT5eGX3sBdCHF" - print(icp.verfers[0].qb64, icp.digers[0].qb64) - assert len(icp.verfers) == 1 - # assert icp.verfers[0].qb64 == "DPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9" - assert len(icp.digers) == 1 - # assert icp.digers[0].qb64 == "EAORnRtObOgNiOlMolji-KijC_isa3lRDpHCsol79cOc" - assert icp.tholder.num == 1 - assert icp.ntholder.num == 1 - - identifiers.addEndRole("aid1", eid=client.agent.pre) - - rbran = b'abcdefghijk0123456789' - rclient = SignifyClient(passcode=rbran, tier=tier) - assert rclient.controller == "EIIY2SgE_bqKLl2MlnREUawJ79jTuucvWwh-S6zsSUFo" - - evt, siger = rclient.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=rclient.ctrl.stem, - pidx=1, - tier=rclient.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - rclient.connect(url=url) - assert rclient.agent is not None - assert rclient.agent.delpre == "EIIY2SgE_bqKLl2MlnREUawJ79jTuucvWwh-S6zsSUFo" - assert rclient.agent.pre == "EFK_y173TSxvwgLHlsWAH9RwRq_6k8R_EVpbJ5rYujbo" - # assert rclient.ctrl.ridx == 0 - - ridentifiers = rclient.identifiers() - op = ridentifiers.create("randy1", algo=Algos.randy) - aid = op["response"] - icp = Serder(ked=aid) - assert len(icp.verfers) == 1 - assert len(icp.verfers) == 1 - assert len(icp.digers) == 1 - assert len(icp.digers) == 1 - assert icp.tholder.num == 1 - assert icp.ntholder.num == 1 - - ridentifiers.addEndRole("randy1", eid=rclient.agent.pre) - print(identifiers.list()) - print(ridentifiers.list()) - - -def test_passcode_rotation(): - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" - - identifiers = client.identifiers() - op = identifiers.create("salty") - aid = op["response"] - sicp = Serder(ked=aid) - - aid = identifiers.get("salty") - assert aid["prefix"] == sicp.pre - - op = identifiers.create("randy", algo=Algos.randy) - aid = op["response"] - ricp = Serder(ked=aid) - - aid = identifiers.get("randy") - assert aid["prefix"] == ricp.pre - - identifiers = client.identifiers() - pres = identifiers.list() - aids = [] - for pre in pres: - aid = identifiers.get(name=pre["name"]) - aids.append(aid) - - client.rotate(nbran='0123456789abcdefghijk', aids=aids) - print(json.dumps(identifiers.list(), indent=1)) - - op = identifiers.rotate("salty") - ked = op["response"] - rot = Serder(ked=ked) - print("Salty Rotated:") - print(rot.pretty()) - - op = identifiers.rotate("randy") - ked = op["response"] - rot = Serder(ked=ked) - print("Randy Rotated:") - print(rot.pretty()) - - -def test_passcode_rotation_x1000(): - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" - - identifiers = client.identifiers() - - for idx in range(1000): - if idx % 10 == 0: - print(f"Created {idx}...") - - algo = random.choice(["salty", "randy"]) - match algo: - case "salty": - op = identifiers.create(f"salty-{idx}") - aid = op["response"] - sicp = Serder(ked=aid) - - op = identifiers.get(f"salty-{idx}") - aid = op["response"] - assert aid["prefix"] == sicp.pre - - case "randy": - op = identifiers.create(f"randy-{idx}", algo=Algos.randy) - aid = op["response"] - ricp = Serder(ked=aid) - - aid = identifiers.get(f"randy-{idx}") - assert aid["prefix"] == ricp.pre - - identifiers = client.identifiers() - pres = identifiers.list() - print(len(pres)) - # aids = [] - # for pre in pres: - # aid = identifiers.get(name=pre["name"]) - # aids.append(aid) - # - # client.rotate(nbran='0123456789abcdefghijk', aids=aids) - # print(json.dumps(identifiers.list(), indent=1)) - # - # ked = identifiers.rotate("salty") - # rot = Serder(ked=ked) - # print("Salty Rotated:") - # print(rot.pretty()) - # - # ked = identifiers.rotate("randy") - # rot = Serder(ked=ked) - # print("Randy Rotated:") - # print(rot.pretty()) - - -def test_recreate_client(): - url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' - tier = Tiers.low - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - - identifiers = client.identifiers() - pres = identifiers.list(limit=1000) - - print(f"loaded {len(pres)} identifiers") - aids = [] - for pre in pres: - aid = identifiers.get(name=pre["name"]) - aids.append(aid) - - print("done getting all values, now to rotate") - - client.rotate(nbran='0123456789abcdefghijk', aids=aids) - - print("rotation done, now to list.") - print(json.dumps(identifiers.list(limit=1000), indent=1)) - - -if __name__ == "__main__": - - #SHOULD WORK - # test_delegation() - # test_witnesses() - # test_salty() - # test_randy() - # test_multisig() - test_query() - # test_recreate_client() - # test_multi_tenant() - - # KNOWN ISSUE with KERIA, will not work - # test_passcode_rotation() - # test_passcode_rotation_x1000() - - #REQUIRES external HSM - # test_extern() \ No newline at end of file diff --git a/integration/app/test_randy.ipynb b/integration/app/test_randy.ipynb deleted file mode 100644 index 8357125..0000000 --- a/integration/app/test_randy.ipynb +++ /dev/null @@ -1,441 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "5ccc5e88-2a8e-4934-a6f5-60500c936ec1", - "metadata": { - "tags": [] - }, - "source": [ - "# SignifyPy Randy Keys\n", - "This notebook should help a developer validate their env setup (Witnesses, KERIA, and SignifyPy client) and walk them step-by-step through the randy key creation. If you haven't setup and run KERI/Witnesses before [Kent Bull's KERI/ACDC guide](https://kentbull.com/2023/03/09/keri-tutorial-series-treasure-hunting-in-abydos-issuing-and-verifying-a-credential-acdc/) is a great starting point" - ] - }, - { - "cell_type": "markdown", - "id": "4f1c272f", - "metadata": {}, - "source": [ - "## Env Setup\n", - "\n", - "### Witness Network\n", - "Run a **clean** local witness network\n", - "* Clear out any of the local files from previous runs. Note keri wants to write to /user/local/var/keri, or ~/.keri\n", - "* And launch the local witness network\n", - "* You will know your witness network isn't clean if you see a KERIA error like:\n", - "``` ValueError: Already incepted pre=DCQE55HKUokSAKc4ntJw2b845r4DnddpPZs8iF_T-0c-. ERR: mdb_txn_renew: MDB_BAD_RSLOT: Invalid reuse of reader locktable slot```\n", - "\n", - "Start your clean demo witness network (usually from the keripy project) with a command like:\n", - "\n", - "```rm -rf /usr/local/var/keri/*;rm -Rf ~/.keri/*;nohup kli witness demo```" - ] - }, - { - "cell_type": "markdown", - "id": "21330ac6", - "metadata": {}, - "source": [ - "## KERIA\n", - "\n", - "Next, start the KERI Agent (KERIA)\n", - "\n", - "In the KERIA project you might want to change start.py to have:\n", - "```help.ogler.level = logging.DEBUG```\n", - "\n", - "When you execute keria start --config-file demo-witness-oobis.json --config-dir ./scripts you need to be in the keria folder where the config files are located (under scripts)\n", - "\n", - "```keria start --config-file demo-witness-oobis.json --config-dir ./scripts``` " - ] - }, - { - "cell_type": "markdown", - "id": "a6ce8ef6", - "metadata": {}, - "source": [ - "\n", - "## Signify\n", - "* If signifypy isn't on PiPy yet, then make sure your Jupyter Python version points to the same Python version where you install signifypy.\n", - "* One way to install signifypy is from git like: ```%python -m pip install \"signifypy @ git+https://github.com/WebOfTrust/signifypy.git\"```" - ] - }, - { - "cell_type": "markdown", - "id": "b52ede76", - "metadata": {}, - "source": [ - "## Install python dependencies from PiPy" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "id": "398aac8e", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "UsageError: Line magic function `%python` not found (But cell magic `%%python` exists, did you mean that instead?).\n" - ] - } - ], - "source": [ - "%python -m pip install keri\n", - "%python -m pip install responses\n", - "%python -m pip install pytest" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "id": "c62e0ed2-19b2-4c82-923d-cf89a8e78c96", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "from time import sleep\n", - "\n", - "import requests\n", - "from keri.app.keeping import Algos\n", - "from keri.core import coring\n", - "from responses import _recorder\n", - "\n", - "import pytest\n", - "from keri import kering\n", - "from keri.core.coring import Tiers, Serder, MtrDex\n", - "\n", - "from signify.app.clienting import SignifyClient" - ] - }, - { - "cell_type": "markdown", - "id": "d71ec345", - "metadata": {}, - "source": [ - "## Basic Signify client creation\n", - "* Create client using the admin url\n", - "* Hard-code the randomness for testing purposes\n", - "* Don't set the tier, to create the least expensive/secure public key" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "id": "921aa899", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Inception event is: b'{\"v\":\"KERI10JSON00012b_\",\"t\":\"icp\",\"d\":\"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose\",\"i\":\"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose\",\"s\":\"0\",\"kt\":\"1\",\"k\":[\"DAbWjobbaLqRB94KiAutAHb_qzPpOHm3LURA_ksxetVc\"],\"nt\":\"1\",\"n\":[\"EIFG_uqfr1yN560LoHYHfvPAhxQ5sN6xZZT_E3h7d2tL\"],\"bt\":\"0\",\"b\":[],\"c\":[],\"a\":[]}'\n" - ] - } - ], - "source": [ - "admin_url = \"http://localhost:3901\"\n", - "# bran = passcode for the db and keystore (habery)\n", - "bran = b'0123456789abcdefghijk'\n", - "# tier (str low|med|high): value from Tierage for security level of stretch\n", - "tier = None\n", - "\n", - "client = SignifyClient(url=admin_url, bran=bran, tier=tier)\n", - "# Your client public key\n", - "assert client.controller == \"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose\"\n", - "# Get the inception event\n", - "serder = client.icp\n", - "# Review the inception event\n", - "assert serder.raw == (b'{\"v\":\"KERI10JSON00012b_\",\"t\":\"icp\",\"d\":\"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJ'\n", - " b'XJHtJose\",\"i\":\"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose\",\"s\":\"0\",\"kt\":\"1'\n", - " b'\",\"k\":[\"DAbWjobbaLqRB94KiAutAHb_qzPpOHm3LURA_ksxetVc\"],\"nt\":\"1\",\"n\":[\"EIFG_u'\n", - " b'qfr1yN560LoHYHfvPAhxQ5sN6xZZT_E3h7d2tL\"],\"bt\":\"0\",\"b\":[],\"c\":[],\"a\":[]}')\n", - "print(\"Inception event is: \", serder.raw)" - ] - }, - { - "cell_type": "markdown", - "id": "3ae85294", - "metadata": {}, - "source": [ - "* For more information about keri fields, see [that section of the KERI whitepaper](https://weboftrust.github.io/ietf-keri/draft-ssmith-keri.html#section-5.1)" - ] - }, - { - "cell_type": "markdown", - "id": "c4e5908c", - "metadata": {}, - "source": [ - "## You can create tiers of clients\n", - "The higher tiers take longer to generate as the number of hash iterations increases" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "id": "a6cf222d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Low tier controller is: ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose\n" - ] - } - ], - "source": [ - "tier = Tiers.low\n", - "client = SignifyClient(url=admin_url, bran=bran, tier=tier)\n", - "print(\"Low tier controller is: \", client.controller)\n", - "assert client.controller == \"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose\"\n", - "\n", - "#tier = Tiers.med\n", - "#client = SignifyClient(url=admin_url, bran=bran, tier=tier)\n", - "#print(\"Medium tier controller is: \", client.controller)\n", - "#assert client.controller == \"EOgQvKz8ziRn7FdR_ebwK9BkaVOnGeXQOJ87N6hMLrK0\"\n", - "\n", - "#tier = Tiers.high\n", - "#client = SignifyClient(url=admin_url, bran=bran, tier=tier)\n", - "#print(\"High tier controller is: \", client.controller)\n", - "#assert client.controller == \"EB8wN2c_tv1WlsJ5c3949-TFWPMB2IflFbdMlZfC_Hgo\"" - ] - }, - { - "cell_type": "markdown", - "id": "f3b94d4b", - "metadata": {}, - "source": [ - "## Post inception event to KERIA (OOB) boot interface" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "id": "701d9d38", - "metadata": {}, - "outputs": [], - "source": [ - "evt, siger = client.ctrl.event()\n", - "res = requests.post(url=\"http://localhost:3903/boot\",\n", - " json=dict(\n", - " icp=evt.ked,\n", - " sig=siger.qb64,\n", - " stem=client.ctrl.stem,\n", - " pidx=1,\n", - " tier=client.ctrl.tier))\n", - "if res.status_code != requests.codes.accepted:\n", - " raise kering.AuthNError(f\"unable to initialize cloud agent connection, {res.status_code}, {res.text}\")" - ] - }, - { - "cell_type": "markdown", - "id": "fb09cf01", - "metadata": {}, - "source": [ - "## Create a KERIA agent session" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "id": "8d8b33e5", - "metadata": {}, - "outputs": [], - "source": [ - "client.connect()\n", - "assert client.agent is not None\n", - "assert client.agent.anchor == \"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose\"\n", - "assert client.agent.pre == \"EJoqUMpQAfqsJhBqv02ehR-9BJYBTCrW8h5JlLdMTWBg\"\n", - "assert client.ctrl.ridx == 0" - ] - }, - { - "cell_type": "markdown", - "id": "32ba696d", - "metadata": {}, - "source": [ - "## Create a KERI Autonomic Identifier (AID)\n", - "* Note that identifiers.create will do the post to KERIA for you " - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "id": "b1d37b67", - "metadata": {}, - "outputs": [], - "source": [ - "identifiers = client.identifiers()\n", - "aids = identifiers.list()\n", - "assert aids == []\n", - "\n", - "aid = identifiers.create(\"aid1\")\n", - "icp = Serder(ked=aid)\n", - "assert icp.pre == \"ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK\"\n", - "assert len(icp.verfers) == 1\n", - "assert icp.verfers[0].qb64 == \"DPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9\"\n", - "assert len(icp.digers) == 1\n", - "assert icp.digers[0].qb64 == \"EAORnRtObOgNiOlMolji-KijC_isa3lRDpHCsol79cOc\"\n", - "assert icp.tholder.num == 1\n", - "assert icp.ntholder.num == 1\n", - "\n", - "rpy = identifiers.makeEndRole(pre=icp.pre, eid=\"EPGaq6inGxOx-VVVEcUb_KstzJZldHJvVsHqD4IPxTWf\")\n", - "\n", - "aids = identifiers.list()\n", - "assert len(aids) == 1\n", - "aid = aids.pop()\n", - "\n", - "salt = aid[Algos.salty]\n", - "assert aid['name'] == \"aid1\"\n", - "assert salt[\"pidx\"] == 0\n", - "assert aid[\"prefix\"] == icp.pre\n", - "assert salt[\"stem\"] == \"signify:aid\"" - ] - }, - { - "cell_type": "markdown", - "id": "def47556", - "metadata": {}, - "source": [ - "## Create a second AID with more configuration" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "id": "26fb711d", - "metadata": {}, - "outputs": [], - "source": [ - "aid2 = identifiers.create(\"aid2\", count=3, ncount=3, isith=\"2\", nsith=\"2\")\n", - "icp2 = Serder(ked=aid2)\n", - "assert icp2.pre == \"EI5e4q43vsTsy-vJFcVGKfI3YKHbOT5ffuseaxtuYydL\"\n", - "assert len(icp2.verfers) == 3\n", - "assert icp2.verfers[0].qb64 == \"DPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9\"\n", - "assert icp2.verfers[1].qb64 == \"DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly\"\n", - "assert icp2.verfers[2].qb64 == \"DEfdjYZMI2hLaHBOpUubn5AUItgOvh2W1vckGE33SIPf\"\n", - "assert len(icp2.digers) == 3\n", - "assert icp2.digers[0].qb64 == \"EEvyqpRLktts-_aSfPHKKv1mTKTV4ngwKKkOaqm3ZuPX\"\n", - "assert icp2.digers[1].qb64 == \"EEkMimwsv_JMZh7k-Rfq5wvhvbEdjVr8NhGQpyssVmNJ\"\n", - "assert icp2.digers[2].qb64 == \"EJy_MjjMWLJkn_5cRaUtDr7asfLe70xbAPD2nablr0iv\"\n", - "assert icp2.tholder.num == 2\n", - "assert icp2.ntholder.num == 2\n", - "\n", - "aids = identifiers.list()\n", - "assert len(aids) == 2\n", - "aid = aids[1]\n", - "assert aid['name'] == \"aid2\"\n", - "assert aid[\"prefix\"] == icp2.pre\n", - "salt = aid[Algos.salty]\n", - "assert salt[\"pidx\"] == 1\n", - "assert salt[\"stem\"] == \"signify:aid\"\n", - "\n", - "ked = identifiers.rotate(\"aid1\")\n", - "rot = Serder(ked=ked)\n", - "\n", - "assert rot.said == \"EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg\"\n", - "assert rot.sn == 1\n", - "assert len(rot.digers) == 1\n", - "assert rot.verfers[0].qb64 == \"DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly\"\n", - "assert rot.digers[0].qb64 == \"EJMovBlrBuD6BVeUsGSxLjczbLEbZU9YnTSud9K4nVzk\"" - ] - }, - { - "cell_type": "markdown", - "id": "dfb84040", - "metadata": {}, - "source": [ - "## Create an interaction event" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "id": "f912e95d", - "metadata": {}, - "outputs": [], - "source": [ - "ked = identifiers.interact(\"aid1\", data=[icp.pre])\n", - "ixn = Serder(ked=ked)\n", - "assert ixn.said == \"ENsmRAg_oM7Hl1S-GTRMA7s4y760lQMjzl0aqOQ2iTce\"\n", - "assert ixn.sn == 2\n", - "assert ixn.ked[\"a\"] == [icp.pre]\n", - "\n", - "aid = identifiers.get(\"aid1\")\n", - "state = aid[\"state\"]\n", - "assert state['s'] == '2'\n", - "assert state['f'] == '2'\n", - "assert state['et'] == 'ixn'\n", - "assert state['d'] == ixn.said\n", - "assert state['ee']['d'] == rot.said" - ] - }, - { - "cell_type": "markdown", - "id": "5d69537e", - "metadata": {}, - "source": [ - "## Review the key events and list the identifiers" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "id": "2f817695", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[{'name': 'aid1', 'prefix': 'ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK', 'salty': {'pidx': 0, 'kidx': 1, 'stem': 'signify:aid', 'tier': 'low', 'dcode': 'E', 'icodes': ['A'], 'ncodes': ['A'], 'transferable': True}}, {'name': 'aid2', 'prefix': 'EI5e4q43vsTsy-vJFcVGKfI3YKHbOT5ffuseaxtuYydL', 'salty': {'pidx': 1, 'kidx': 0, 'stem': 'signify:aid', 'tier': 'low', 'dcode': 'E', 'icodes': ['A', 'A', 'A'], 'ncodes': ['A', 'A', 'A'], 'transferable': True}}]\n" - ] - } - ], - "source": [ - "events = client.keyEvents()\n", - "log = events.get(pre=aid[\"prefix\"])\n", - "assert len(log) == 3\n", - "serder = coring.Serder(ked=log[0])\n", - "assert serder.pre == icp.pre\n", - "assert serder.said == icp.said\n", - "serder = coring.Serder(ked=log[1])\n", - "assert serder.pre == rot.pre\n", - "assert serder.said == rot.said\n", - "serder = coring.Serder(ked=log[2])\n", - "assert serder.pre == ixn.pre\n", - "assert serder.said == ixn.said\n", - "\n", - "print(identifiers.list())" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.11.3 64-bit ('3.11.3')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.3" - }, - "vscode": { - "interpreter": { - "hash": "ebe7f73a47762276d07c618e3a56dc3bc1e0329af7b3d7764336a2d1f3e9db09" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..5f0fd26 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = --ignore=scripts --ignore=integration --ignore=docs \ No newline at end of file diff --git a/scripts/create_agent.py b/scripts/create_agent.py index 84eb6f8..366311f 100644 --- a/scripts/create_agent.py +++ b/scripts/create_agent.py @@ -39,6 +39,7 @@ def create_agent(): stem=client.ctrl.stem, pidx=1, tier=client.ctrl.tier)) + print(res.json()) if res.status_code != requests.codes.accepted: raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") diff --git a/scripts/create_new_quartet.py b/scripts/create_new_quartet.py new file mode 100644 index 0000000..d26c3c7 --- /dev/null +++ b/scripts/create_new_quartet.py @@ -0,0 +1,158 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +from time import sleep + +import requests +from keri import kering +from keri.core import coring, serdering +from keri.core.coring import Tiers + +from signify.app.clienting import SignifyClient + +URL = "http://localhost:3901" + + +def create_quadlet(): + bran1 = "0123456789abcdefghsec" + bran2 = "0123456789abcdefghsaw" + bran3 = "0123456789abcdefghsg4" + bran4 = "0123456789abcdefgh0z5" + + client1 = create_agent(bran1, + "EIX9c9DUvzXK5E7EnBybtIizdo9KBQISOrYb0JNQ7OFI", + "EHkbqFLxa5TrSjAiZsC5gtHmynGYkUhgOyNWDRKbfSPd") + + client2 = create_agent(bran2, + "EKzJM2IBFSkAIz-RpXUv7GhvTdPPvpNl9nf8emPUcXqi", + "EAiO7QUJcyDXJ5WfToUJqJV437N5p9NXbpMXb9XEjUti") + + client3 = create_agent(bran3, + "ECyAJ3zRVKO5vr8LbssPnNEKf10MmgF9BGqB06lvFVR0", + "EMY8KpPNknneXlrhGIFnN7UUOMO27g-M5SO5MRqXPzwe") + + client4 = create_agent(bran4, + "ENL1pd5iTRBe6XMMD-yi05W6_bVz1nQFvxWM52I6DTRk", + "EHLxs9z_5xwLuRBuS-xkQwIJvZMJ6d9dhxSo9fYn8lCs") + + create_aid(client1, "multisig1", bran1, "EBFg-5SGDCv5YfwpkArWRBdTxNRUXU8uVcDKNzizOQZc") + create_aid(client2, "multisig2", bran2, "EBmW2bXbgsP3HITwW3FmITzAb3wVmHlxCusZ46vgGgP5") + create_aid(client3, "multisig3", bran3, "EL4RpdS2Atb2Syu5xLdpz9CcNNYoFUUDlLHxHD09vcgh") + create_aid(client4, "multisig4", bran4, "EAiBVuuhCZrgckeHc9KzROVGJpmGbk2-e1B25GaeRrJs") + + oobi(client2, "multisig1", "http://127.0.0.1:3902/oobi/EBFg-5SGDCv5YfwpkArWRBdTxNRUXU8uVcDKNzizOQZc") + oobi(client3, "multisig1", "http://127.0.0.1:3902/oobi/EBFg-5SGDCv5YfwpkArWRBdTxNRUXU8uVcDKNzizOQZc") + oobi(client4, "multisig1", "http://127.0.0.1:3902/oobi/EBFg-5SGDCv5YfwpkArWRBdTxNRUXU8uVcDKNzizOQZc") + + oobi(client1, "multisig2", "http://127.0.0.1:3902/oobi/EBmW2bXbgsP3HITwW3FmITzAb3wVmHlxCusZ46vgGgP5") + oobi(client3, "multisig2", "http://127.0.0.1:3902/oobi/EBmW2bXbgsP3HITwW3FmITzAb3wVmHlxCusZ46vgGgP5") + oobi(client4, "multisig2", "http://127.0.0.1:3902/oobi/EBmW2bXbgsP3HITwW3FmITzAb3wVmHlxCusZ46vgGgP5") + + oobi(client1, "multisig3", "http://127.0.0.1:3902/oobi/EL4RpdS2Atb2Syu5xLdpz9CcNNYoFUUDlLHxHD09vcgh") + oobi(client2, "multisig3", "http://127.0.0.1:3902/oobi/EL4RpdS2Atb2Syu5xLdpz9CcNNYoFUUDlLHxHD09vcgh") + oobi(client4, "multisig3", "http://127.0.0.1:3902/oobi/EL4RpdS2Atb2Syu5xLdpz9CcNNYoFUUDlLHxHD09vcgh") + + oobi(client1, "multisig4", "http://127.0.0.1:3902/oobi/EAiBVuuhCZrgckeHc9KzROVGJpmGbk2-e1B25GaeRrJs") + oobi(client2, "multisig4", "http://127.0.0.1:3902/oobi/EAiBVuuhCZrgckeHc9KzROVGJpmGbk2-e1B25GaeRrJs") + oobi(client3, "multisig4", "http://127.0.0.1:3902/oobi/EAiBVuuhCZrgckeHc9KzROVGJpmGbk2-e1B25GaeRrJs") + + oobi(client1, "old-multisig1", "http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness") + oobi(client1, "old-multisig2", "http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness") + oobi(client1, "old-multisig3", "http://127.0.0.1:5642/oobi/EMkvHBDM2n9rvjnUiLvdAFJjNZ81Fp0QmEgto-2cG8CS/witness") + oobi(client1, "old-multisig4", "http://127.0.0.1:5642/oobi/EAV9iv9aFLy2AULDisAfeHgLy1-NmKP6fEVddYAE7dyf/witness") + + oobi(client2, "old-multisig1", "http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness") + oobi(client2, "old-multisig2", "http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness") + oobi(client2, "old-multisig3", "http://127.0.0.1:5642/oobi/EMkvHBDM2n9rvjnUiLvdAFJjNZ81Fp0QmEgto-2cG8CS/witness") + oobi(client2, "old-multisig4", "http://127.0.0.1:5642/oobi/EAV9iv9aFLy2AULDisAfeHgLy1-NmKP6fEVddYAE7dyf/witness") + + oobi(client3, "old-multisig1", "http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness") + oobi(client3, "old-multisig2", "http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness") + oobi(client3, "old-multisig3", "http://127.0.0.1:5642/oobi/EMkvHBDM2n9rvjnUiLvdAFJjNZ81Fp0QmEgto-2cG8CS/witness") + oobi(client3, "old-multisig4", "http://127.0.0.1:5642/oobi/EAV9iv9aFLy2AULDisAfeHgLy1-NmKP6fEVddYAE7dyf/witness") + + oobi(client4, "old-multisig1", "http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness") + oobi(client4, "old-multisig2", "http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness") + oobi(client4, "old-multisig3", "http://127.0.0.1:5642/oobi/EMkvHBDM2n9rvjnUiLvdAFJjNZ81Fp0QmEgto-2cG8CS/witness") + oobi(client4, "old-multisig4", "http://127.0.0.1:5642/oobi/EAV9iv9aFLy2AULDisAfeHgLy1-NmKP6fEVddYAE7dyf/witness") + + oobi(client1, "multisig", "http://127.0.0.1:5642/oobi/EDWg3-rB5FTpcckaYdBcexGmbLIO6AvAwjaJTBlXUn_I/witness") + oobi(client2, "multisig", "http://127.0.0.1:5642/oobi/EDWg3-rB5FTpcckaYdBcexGmbLIO6AvAwjaJTBlXUn_I/witness") + oobi(client3, "multisig", "http://127.0.0.1:5642/oobi/EDWg3-rB5FTpcckaYdBcexGmbLIO6AvAwjaJTBlXUn_I/witness") + oobi(client4, "multisig", "http://127.0.0.1:5642/oobi/EDWg3-rB5FTpcckaYdBcexGmbLIO6AvAwjaJTBlXUn_I/witness") + + +def create_agent(bran, ctrlAid, agentAid): + tier = Tiers.low + client = SignifyClient(passcode=bran, tier=tier) + assert client.controller == ctrlAid + + evt, siger = client.ctrl.event() + + res = requests.post(url="http://localhost:3903/boot", + json=dict( + icp=evt.ked, + sig=siger.qb64, + stem=client.ctrl.stem, + pidx=1, + tier=client.ctrl.tier)) + + if res.status_code != requests.codes.accepted: + raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") + + client.connect(url=URL, ) + assert client.agent is not None + + assert client.agent.pre == agentAid + assert client.agent.delpre == ctrlAid + print(f"Agent {client.agent.pre} created for controller {client.controller}") + + return client + + +def create_aid(client, name, bran, pre): + identifiers = client.identifiers() + operations = client.operations() + + wits = [ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ] + + op = identifiers.create(name, bran=bran, wits=wits, toad="2") + op = op[2] + + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + icp = serdering.SerderKERI(sad=op["response"]) + print(icp.pre) + assert icp.pre == pre + + identifiers.addEndRole(name, eid=client.agent.pre) + + print(f"Created AID {name}: {icp.pre}") + + +def oobi(client, alias, url): + oobis = client.oobis() + operations = client.operations() + + print(f"Resolving oobi to {alias}") + op = oobis.resolve( + oobi=url, + alias=alias) + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + print("... done") + + +if __name__ == "__main__": + create_quadlet() diff --git a/scripts/create_person_aid.py b/scripts/create_person_aid.py index b84afba..df2adc4 100644 --- a/scripts/create_person_aid.py +++ b/scripts/create_person_aid.py @@ -7,7 +7,7 @@ """ from time import sleep -from keri.core import coring +from keri.core import serdering from keri.core.coring import Tiers from signify.app.clienting import SignifyClient @@ -25,7 +25,7 @@ def create_aid(): oobis = client.oobis() aids = identifiers.list() - assert aids == [] + assert aids['aids'] == [] wits = [ "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", @@ -34,12 +34,13 @@ def create_aid(): ] op = identifiers.create("BankUser", bran="0123456789abcdefghijk", wits=wits, toad="2") + op = op[2] while not op["done"]: op = operations.get(op["name"]) sleep(1) - icp = coring.Serder(ked=op["response"]) + icp = serdering.SerderKERI(sad=op["response"]) assert icp.pre == "EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk" print(f"Person AID {icp.pre} created") diff --git a/scripts/data/qvi-data.json b/scripts/data/qvi-data.json index 05e68b0..07e7fc4 100644 --- a/scripts/data/qvi-data.json +++ b/scripts/data/qvi-data.json @@ -1,3 +1,3 @@ { - "LEI": "6383001AJTYIGC8Y1X37" + "LEI": "5493001KJTIIGC8Y1R17" } \ No newline at end of file diff --git a/scripts/init_agent.py b/scripts/init_agent.py index 7b796ce..5434f24 100644 --- a/scripts/init_agent.py +++ b/scripts/init_agent.py @@ -9,7 +9,7 @@ import pytest import requests from keri import kering -from keri.core import coring +from keri.core import coring, serdering from keri.core.coring import Tiers from signify.app.clienting import SignifyClient @@ -21,7 +21,7 @@ def create_agent(): stem = "signify:controller" ims = input("Type of paste controller inception event:") - serder = coring.Serder(raw=ims.encode("utf-8")) + serder = serdering.SerderKERI(raw=ims.encode("utf-8")) siger = coring.Siger(qb64=ims[serder.size:]) res = requests.post(url="http://localhost:3903/boot", diff --git a/scripts/issue-ecr.sh b/scripts/issue-ecr.sh index 0d0b44e..196da74 100755 --- a/scripts/issue-ecr.sh +++ b/scripts/issue-ecr.sh @@ -41,26 +41,50 @@ kli vc registry incept --name qvi --alias qvi --registry-name vLEI-qvi kli vc registry incept --name legal-entity --alias legal-entity --registry-name vLEI-legal-entity # Issue QVI credential vLEI from GLEIF External to QVI -kli vc issue --name external --alias external --registry-name vLEI-external --schema EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao --recipient EHMnCf8_nIemuPx-cUHaDQq8zSnQIFAurdEpwHpNbnvX --data @"${KERI_DEMO_SCRIPT_DIR}"/data/qvi-data.json -kli vc list --name qvi --alias qvi --poll +kli vc create --name external --alias external --registry-name vLEI-external --schema EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao --recipient EHMnCf8_nIemuPx-cUHaDQq8zSnQIFAurdEpwHpNbnvX --data @"${KERI_DEMO_SCRIPT_DIR}"/data/qvi-data.json +SAID=$(kli vc list --name external --alias external --issued --said --schema EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao) +kli ipex grant --name external --alias external --said "${SAID}" --recipient EHMnCf8_nIemuPx-cUHaDQq8zSnQIFAurdEpwHpNbnvX +GRANT=$(kli ipex list --name qvi --alias qvi --poll --said) +echo "Admitting credential from grant ${GRANT}" +kli ipex admit --name qvi --alias qvi --said "${GRANT}" +kli vc list --name qvi --alias qvi # Issue LE credential from QVI to Legal Entity - have to create the edges first QVI_SAID=$(kli vc list --name qvi --alias qvi --said --schema EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao) echo \"$QVI_SAID\" | jq -f "${KERI_DEMO_SCRIPT_DIR}"/data/legal-entity-edges-filter.jq > /tmp/legal-entity-edges.json kli saidify --file /tmp/legal-entity-edges.json -kli vc issue --name qvi --alias qvi --registry-name vLEI-qvi --schema ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY --recipient EIitNxxiNFXC1HDcPygyfyv3KUlBfS_Zf-ZYOvwjpTuz --data @"${KERI_DEMO_SCRIPT_DIR}"/data/legal-entity-data.json --edges @/tmp/legal-entity-edges.json --rules @"${KERI_DEMO_SCRIPT_DIR}"/data/rules.json -kli vc list --name legal-entity --alias legal-entity --poll +kli vc create --name qvi --alias qvi --registry-name vLEI-qvi --schema ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY --recipient EIitNxxiNFXC1HDcPygyfyv3KUlBfS_Zf-ZYOvwjpTuz --data @"${KERI_DEMO_SCRIPT_DIR}"/data/legal-entity-data.json --edges @/tmp/legal-entity-edges.json --rules @"${KERI_DEMO_SCRIPT_DIR}"/data/rules.json +SAID=$(kli vc list --name qvi --alias qvi --issued --said --schema ENPXp1vQzRF6JwIuS-mp2U8Uf1MoADoP_GqQ62VsDZWY) +kli ipex grant --name qvi --alias qvi --said "${SAID}" --recipient EIitNxxiNFXC1HDcPygyfyv3KUlBfS_Zf-ZYOvwjpTuz +GRANT=$(kli ipex list --name legal-entity --alias legal-entity --poll --said) +echo "Admitting credential from grant ${GRANT}" +kli ipex admit --name legal-entity --alias legal-entity --said "${GRANT}" +kli vc list --name legal-entity --alias legal-entity # Issue ECR Authorization credential from Legal Entity to QVI - have to create the edges first LE_SAID=$(kli vc list --name legal-entity --alias legal-entity --said) echo \"$LE_SAID\" | jq -f "${KERI_DEMO_SCRIPT_DIR}"/data/ecr-auth-edges-filter.jq > /tmp/ecr-auth-edges.json kli saidify --file /tmp/ecr-auth-edges.json -kli vc issue --name legal-entity --alias legal-entity --registry-name vLEI-legal-entity --schema EH6ekLjSr8V32WyFbGe1zXjTzFs9PkTYmupJ9H65O14g --recipient EHMnCf8_nIemuPx-cUHaDQq8zSnQIFAurdEpwHpNbnvX --data @"${KERI_DEMO_SCRIPT_DIR}"/data/ecr-auth-data.json --edges @/tmp/ecr-auth-edges.json --rules @"${KERI_DEMO_SCRIPT_DIR}"/data/ecr-auth-rules.json -kli vc list --name qvi --alias qvi --poll +kli vc create --name legal-entity --alias legal-entity --registry-name vLEI-legal-entity --schema EH6ekLjSr8V32WyFbGe1zXjTzFs9PkTYmupJ9H65O14g --recipient EHMnCf8_nIemuPx-cUHaDQq8zSnQIFAurdEpwHpNbnvX --data @"${KERI_DEMO_SCRIPT_DIR}"/data/ecr-auth-data.json --edges @/tmp/ecr-auth-edges.json --rules @"${KERI_DEMO_SCRIPT_DIR}"/data/ecr-auth-rules.json +SAID=$(kli vc list --name legal-entity --alias legal-entity --issued --said --schema EH6ekLjSr8V32WyFbGe1zXjTzFs9PkTYmupJ9H65O14g) +kli ipex grant --name legal-entity --alias legal-entity --said "${SAID}" --recipient EHMnCf8_nIemuPx-cUHaDQq8zSnQIFAurdEpwHpNbnvX +GRANT=$(kli ipex list --name legal-entity --alias legal-entity --sent --type grant --said) +kli ipex list --name qvi --alias qvi --poll +echo "Admitting credential from grant ${GRANT}" +kli ipex admit --name qvi --alias qvi --said "${GRANT}" +kli vc list --name qvi --alias qvi # Issue ECR credential from QVI to Person AUTH_SAID=$(kli vc list --name qvi --alias qvi --said --schema EH6ekLjSr8V32WyFbGe1zXjTzFs9PkTYmupJ9H65O14g) echo "[\"$QVI_SAID\", \"$AUTH_SAID\"]" | jq -f "${KERI_DEMO_SCRIPT_DIR}"/data/ecr-edges-filter.jq > /tmp/ecr-edges.json kli saidify --file /tmp/ecr-edges.json -kli vc issue --name qvi --alias qvi --private --registry-name vLEI-qvi --schema EEy9PkikFcANV1l7EHukCeXqrzT1hNZjGlUk7wuMO5jw --recipient EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk --data @"${KERI_DEMO_SCRIPT_DIR}"/data/ecr-data.json --edges @/tmp/ecr-edges.json --rules @"${KERI_DEMO_SCRIPT_DIR}"/data/ecr-rules.json +kli vc create --name qvi --alias qvi --private --registry-name vLEI-qvi --schema EEy9PkikFcANV1l7EHukCeXqrzT1hNZjGlUk7wuMO5jw --recipient EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk --data @"${KERI_DEMO_SCRIPT_DIR}"/data/ecr-data.json --edges @/tmp/ecr-edges.json --rules @"${KERI_DEMO_SCRIPT_DIR}"/data/ecr-rules.json +SAID=$(kli vc list --name qvi --alias qvi --issued --said --schema EEy9PkikFcANV1l7EHukCeXqrzT1hNZjGlUk7wuMO5jw) +kli ipex grant --name qvi --alias qvi --said "${SAID}" --recipient EBcIURLpxmVwahksgrsGW6_dUw0zBhyEHYFk17eWrZfk +kli ipex list --name qvi --alias qvi --poll + +GRANT=$(python "${KERI_SCRIPT_DIR}"/list_ipex.py) +echo "Sending admit with ${GRANT}" +python "${KERI_SCRIPT_DIR}"/send_admit.py "${GRANT}" EHMnCf8_nIemuPx-cUHaDQq8zSnQIFAurdEpwHpNbnvX +kli ipex list --name qvi --alias qvi --poll python "${KERI_SCRIPT_DIR}"/list_person_credentials.py diff --git a/scripts/join_new_quadlet.py b/scripts/join_new_quadlet.py new file mode 100644 index 0000000..ec10bc7 --- /dev/null +++ b/scripts/join_new_quadlet.py @@ -0,0 +1,96 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +join new quadlet script + +""" + +from time import sleep + +from keri.core import eventing, coring, serdering +from keri.core.coring import Tiers +from signify.app.clienting import SignifyClient + + +def join_new_quadlet(): + group = "multisig" + client1 = get_client(bran=b'0123456789abcdefghsec') + client2 = get_client(bran=b'0123456789abcdefghsaw') + + op2 = accept_join_request(client2, name="multisig2", group=group) + if not op2: + raise ValueError("No op created for multisig2") + op1 = accept_join_request(client1, name="multisig1", group=group) + if not op1: + raise ValueError("No op created for multisig1") + + while not op2['done']: + sleep(1) + op2 = client2.operations().get(op2['name']) + + while not op1['done']: + sleep(1) + op1 = client1.operations().get(op1['name']) + + +def get_client(bran): + url = "http://localhost:3901" + tier = Tiers.low + + return SignifyClient(passcode=bran, tier=tier, url=url) + + +def accept_join_request(client, name, group): + identifiers = client.identifiers() + notificatons = client.notifications() + exchanges = client.exchanges() + groups = client.groups() + + hab = identifiers.get(name) + + resp = notificatons.list() + + for note in resp['notes']: + payload = note['a'] + route = payload['r'] + if route == "/multisig/rot": + said = payload['d'] + + res = exchanges.get(said=said) + exn = res['exn'] + a = exn['a'] + + gid = a['gid'] + smids = a['smids'] + rmids = a['rmids'] + + recipients = list(set(smids + (rmids or []))) + recipients.remove(hab['prefix']) + + idx = smids.index(hab["prefix"]) + odx = rmids.index(hab['prefix']) + + embeds = exn['e'] + ked = embeds['rot'] + rot = serdering.SerderKERI(sad=ked) + + keeper = client.manager.get(aid=hab) + sigs = keeper.sign(ser=rot.raw, indexed=True, indices=[idx], ondices=[odx]) + + op = groups.join(group, rot, sigs, gid, smids, rmids) + + embeds = dict( + rot=eventing.messagize(serder=rot, sigers=[coring.Siger(qb64=sig) for sig in sigs]) + ) + + exchanges.send(name, "multisig", sender=hab, route="/multisig/rot", + payload=dict(gid=gid, smids=smids, rmids=smids), + embeds=embeds, recipients=recipients) + + return op + + return None + + +if __name__ == "__main__": + join_new_quadlet() diff --git a/scripts/list_contacts.py b/scripts/list_contacts.py new file mode 100644 index 0000000..8c65e01 --- /dev/null +++ b/scripts/list_contacts.py @@ -0,0 +1,28 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +import json + +from keri.core.coring import Tiers +from signify.app.clienting import SignifyClient + + +def list_contacts(): + url = "http://localhost:3901" + bran = b'0123456789abcdefghsaw' + tier = Tiers.low + + client = SignifyClient(passcode=bran, tier=tier, url=url) + contacts = client.contacts() + + cons = contacts.list() + + print(json.dumps(cons)) + + +if __name__ == "__main__": + list_contacts() diff --git a/scripts/list_ipex.py b/scripts/list_ipex.py new file mode 100644 index 0000000..2f45412 --- /dev/null +++ b/scripts/list_ipex.py @@ -0,0 +1,29 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +from keri.core.coring import Tiers + +from signify.app.clienting import SignifyClient + + +def list_ipex(): + url = "http://localhost:3901" + bran = b'0123456789abcdefghijk' + tier = Tiers.low + + client = SignifyClient(passcode=bran, tier=tier, url=url) + notificatons = client.notifications() + + notes = notificatons.list() + for note in notes["notes"]: + a = note['a'] + if a['r'].startswith("/exn/ipex/"): + print(a['d']) + + +if __name__ == "__main__": + list_ipex() diff --git a/scripts/list_kevers.py b/scripts/list_kevers.py new file mode 100644 index 0000000..9870b9a --- /dev/null +++ b/scripts/list_kevers.py @@ -0,0 +1,29 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +import json + +from keri.core.coring import Tiers +from signify.app.clienting import SignifyClient + + +def list_contacts(): + url = "http://localhost:3901" + bran = b'0123456789abcdefghsaw' + tier = Tiers.low + + client = SignifyClient(passcode=bran, tier=tier, url=url) + keyStates = client.keyStates() + + multisig1 = keyStates.get("EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4") + multisig2 = keyStates.get("EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1") + + print(json.dumps([multisig1, multisig2])) + + +if __name__ == "__main__": + list_contacts() diff --git a/scripts/list_notifications.py b/scripts/list_notifications.py new file mode 100644 index 0000000..cea76e2 --- /dev/null +++ b/scripts/list_notifications.py @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +from keri.core import coring +from keri.core.coring import Tiers +from signify.app.clienting import SignifyClient + + +def list_notifications(): + url = "http://localhost:3901" + bran = b'0123456789abcdefghsaw' + tier = Tiers.low + + client = SignifyClient(passcode=bran, tier=tier, url=url) + notificatons = client.notifications() + + notes = notificatons.list() + print(notes) + + +if __name__ == "__main__": + list_notifications() diff --git a/scripts/list_person_credentials.py b/scripts/list_person_credentials.py index 3e013a2..c78cde9 100644 --- a/scripts/list_person_credentials.py +++ b/scripts/list_person_credentials.py @@ -5,33 +5,39 @@ Testing clienting with integration tests that require a running KERIA Cloud Agent """ - +from keri.core import serdering from keri.core.coring import Tiers -from keri.vc.proving import Creder from signify.app.clienting import SignifyClient -from signify.app.credentialing import CredentialTypes def list_credentials(): url = "http://localhost:3901" - bran = b'0123456789abcdefghijk' + # bran = b'0123456789abcdefghijk' + # bran = b'PoLT1X6fDQliXyCuzCVuv' + bran = b'Pwt6yLXRSs7IjZ23tRHIV' tier = Tiers.low client = SignifyClient(passcode=bran, tier=tier, url=url) identifiers = client.identifiers() - aids = identifiers.list() + res = identifiers.list() + + aids = res['aids'] + + assert len(aids) == 2 + + res = identifiers.get("holder") - assert len(aids) == 1 - aid = aids[0]['prefix'] + aid = res['prefix'] print(aid) credentials = client.credentials() - creds = credentials.list("BankUser", filtr={'-a-i': aid}) + creds = credentials.list(filtr={'-a-i': aid}) + # print(creds) assert len(creds) == 1 - creder = Creder(ked=creds[0]['sad']) + creder = serdering.SerderACDC(sad=creds[0]['sad']) print(creder.pretty(size=5000)) # said = creder.said diff --git a/scripts/multisig-create-credential.py b/scripts/multisig-create-credential.py new file mode 100644 index 0000000..2f38cf1 --- /dev/null +++ b/scripts/multisig-create-credential.py @@ -0,0 +1,204 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +from time import sleep + +import requests +from keri import kering +from keri.app import signing +from keri.app.keeping import Algos +from keri.core import coring, eventing, serdering +from keri.core.coring import Tiers +from signify.app.clienting import SignifyClient + +TIME = "2023-09-25T16:01:37.000000+00:00" + + +def create_credential(): + url = "http://localhost:3901" + bran = b'0123456789abcdefghijk' + tier = Tiers.low + + client = SignifyClient(passcode=bran, tier=tier) + print(client.controller) + assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" + + evt, siger = client.ctrl.event() + res = requests.post(url="http://localhost:3903/boot", + json=dict( + icp=evt.ked, + sig=siger.qb64, + stem=client.ctrl.stem, + pidx=1, + tier=client.ctrl.tier)) + + client.connect(url=url) + assert client.agent is not None + assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" + assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" + # assert client.ctrl.ridx == 0 + + if res.status_code != requests.codes.accepted: + raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") + + oobis = client.oobis() + identifiers = client.identifiers() + registries = client.registries() + credentials = client.credentials() + exchanges = client.exchanges() + operations = client.operations() + ipex = client.ipex() + + (_, _, op) = identifiers.create("multisig3", bran="0123456789lmnopqrstuv") + icp = op["response"] + serder = serdering.SerderKERI(sad=icp) + assert serder.pre == "EOGvmhJDBbJP4zeXaRun5vSz0O3_1zB10DwNMyjXlJEv" + print(f"created AID {serder.pre}") + + identifiers.addEndRole("multisig3", eid=client.agent.pre) + + print(f"OOBI for {serder.pre}:") + oobi = oobis.get("multisig3") + print(oobi) + + op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness", + alias="multisig1") + + print("resolving oobi for multisig1") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + print("... done") + + multisig1 = op["response"] + print("resolving oobi for multisig2") + op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness", + alias="multisig2") + print("... done") + + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + multisig2 = op["response"] + + m3 = identifiers.get("multisig3") + agent0 = m3["state"] + print(f"agent is {agent0}") + + states = rstates = [multisig2, multisig1, agent0] + + icp, isigs, op = identifiers.create("multisig", algo=Algos.group, mhab=m3, + isith=["1/3", "1/3", "1/3"], nsith=["1/3", "1/3", "1/3"], + toad=3, + wits=[ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + states=states, + rstates=rstates) + + smids = [state['i'] for state in states] + recp = [state['i'] for state in [multisig2, multisig1]] + + embeds = dict( + icp=eventing.messagize(serder=icp, sigers=[coring.Siger(qb64=sig) for sig in isigs]) + ) + + exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/icp", + payload=dict(gid=icp.pre, smids=smids, rmids=smids), + embeds=embeds, recipients=recp) + + print("waiting on multisig creation...") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + gAid = op["response"] + print(f"group multisig created {gAid}") + + op = oobis.resolve(oobi="http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao", + alias="vc") + + print("resolving oobi for credential schema") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + print("... done") + + m = identifiers.get("multisig") + + vcp, anc, rsigs, op = registries.create(hab=m, registryName="vLEI", + nonce="AHSNDV3ABI6U8OIgKaj3aky91ZpNL54I5_7-qwtC6q2s") + + embeds = dict( + vcp=vcp.raw, + anc=eventing.messagize(serder=anc, sigers=[coring.Siger(qb64=sig) for sig in rsigs]) + ) + + recp = ["EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4", "EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1"] + exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/vcp", + payload=dict(gid=m["prefix"], usage="Issue vLEIs"), + embeds=embeds, recipients=recp) + + print("waiting on credential registry creation...") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + print("registry created") + + registry = registries.get(name="multisig", registryName="vLEI") + + m = identifiers.get("multisig") + data = { + "LEI": "5493001KJTIIGC8Y1R17" + } + creder, iserder, anc, sigs, op = credentials.create(m, registry, data=data, + schema="EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao", + recipient="ELjSFdrTdCebJlmvbFNX9-TLhR2PO0_60al1kQp5_e6k", + timestamp=TIME) + + prefixer = coring.Prefixer(qb64=iserder.pre) + seqner = coring.Seqner(sn=iserder.sn) + acdc = signing.serialize(creder, prefixer, seqner, coring.Saider(qb64=iserder.said)) + iss = registries.serialize(iserder, anc) + + embeds = dict( + acdc=acdc, + iss=iss, + anc=eventing.messagize(serder=anc, sigers=[coring.Siger(qb64=sig) for sig in sigs]) + ) + exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/iss", + payload=dict(gid=m["prefix"]), + embeds=embeds, recipients=recp) + + print("waiting on credential creation...") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + m = identifiers.get("multisig") + grant, sigs, end = ipex.grant(m, recp="ELjSFdrTdCebJlmvbFNX9-TLhR2PO0_60al1kQp5_e6k", acdc=acdc, + iss=iss, message="", + anc=eventing.messagize(serder=anc, sigers=[coring.Siger(qb64=sig) for sig in sigs]), + dt=TIME) + + mstate = m["state"] + seal = eventing.SealEvent(i=m["prefix"], s=mstate["ee"]["s"], d=mstate["ee"]["d"]) + ims = eventing.messagize(serder=grant, sigers=[coring.Siger(qb64=sig) for sig in sigs], seal=seal) + ims.extend(end) + embeds = dict( + exn=ims + ) + + exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/exn", + payload=dict(gid=m["prefix"]), + embeds=embeds, recipients=recp) + + +if __name__ == "__main__": + create_credential() diff --git a/scripts/multisig-endrole.py b/scripts/multisig-endrole.py deleted file mode 100644 index d14db93..0000000 --- a/scripts/multisig-endrole.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- encoding: utf-8 -*- -""" -SIGNIFY -signify.app.clienting module - -Testing clienting with integration tests that require a running KERIA Cloud Agent -""" - -import json -from time import sleep - -from keri.app.keeping import Algos -from keri.core.coring import Tiers -from signify.app.clienting import SignifyClient - - -def stream_escrows(): - url = "http://localhost:3901" - bran = b'9876543210abcdefghijk' - tier = Tiers.low - - client = SignifyClient(passcode=bran, tier=tier, url=url) - identifiers = client.identifiers() - - members = identifiers.members("multisig") - - auths = {} - for member in members['signing']: - print(member["aid"]) - ends = member["ends"] - - if not ends: - print("\tNone") - - if "agent" in ends: - for k in ends['agent']: - - print("\tAgent: ", k) - - - # endroles = client.endroles() - # print(endroles.list(name="multisig-sigpy")) - # print(endroles.list(aid="ELViLL4JCh-oktYca-pmPLwkmUaeYjyPmCLxELAKZW8V")) - - # escrows = client.escrows() - # - # for rpy in escrows.getEscrowReply(route="/end/role"): - # print(rpy) - - -if __name__ == "__main__": - stream_escrows() diff --git a/scripts/multisig-holder.py b/scripts/multisig-holder.py new file mode 100644 index 0000000..d08b8dc --- /dev/null +++ b/scripts/multisig-holder.py @@ -0,0 +1,349 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +import json +from time import sleep + +import requests +from keri import kering +from keri.app import signing +from keri.app.keeping import Algos +from keri.core import coring, eventing, serdering +from keri.core.coring import Tiers +from keri.help import helping + +from signify.app.clienting import SignifyClient + +TIME = "2023-10-15T16:01:37.000000+00:00" + +def multisig_holder(): + print("Creating issuer agent") + client0 = create_agent(b'Dmopaoe5tANSD8A5rwIhW', + "EGTZsyZyREvrD-swB4US5n-1r7h-40sVPIrmS14ixuoJ", + "EPkVulMF7So04EJqUDmmHu6SkllpbOt-KJOnSwckmXwz") + + print("Creating issuer AID") + create_aid(client0, "issuer", "W1OnK0b5rKq6TcKBWhsQa", "ELTkSY_C70Qj8SbPh7F121Q3iA_zNlt8bS-pzOMiCBgG") + add_end_role(client0, "issuer") + issuer = get_aid(client0, "issuer") + + print("Creating holder1 agent") + client1 = create_agent(b'PoLT1X6fDQliXyCuzCVuv', + "EBqP5_kfQIsBWPWSKOL0iiaDv-nwVvNsN0YHP7SYKK2u", + "ENEDfnaIJyB-ITwEZGv559Mzdk0lNng3UaQKJWzFoTK0") + + print("Creating holder1 AID") + create_aid(client1, "holder1", "B-GzoqRMFLGtV0Zy0Jajw", "ENIatcaOLTJ3AMCbv0ZiTXR-2HGrJAwsyXVKhQpwuaIq") + add_end_role(client1, "holder1") + + print("Creating holder2 agent") + client2 = create_agent(b'Pwt6yLXRSs7IjZ23tRHIV', + "EA-SUezF76zn7zF7so-T-DF8FsvI9vO1mtOhWjbdRsqK", + "EBn32S-PTYCVZWIhE4jT0l9-23suzNs2z7raYf0YpOSb") + print("Creating holder2 AID") + create_aid(client2, "holder2", "AAuXz_5CvLOXMCtZ1prCS", "EBlzZyyDM2wBzPLPKO0RiMGbYJ1PuryD1-zQOr9fKctV") + add_end_role(client2, "holder2") + + print("Resolving OOBIs") + holder2 = resolve_oobi(client1, "holder2", + "http://127.0.0.1:3902/oobi/EBlzZyyDM2wBzPLPKO0RiMGbYJ1PuryD1-zQOr9fKctV/agent/" + "EBn32S-PTYCVZWIhE4jT0l9-23suzNs2z7raYf0YpOSb") + resolve_oobi(client1, "issuer", + "http://127.0.0.1:3902/oobi/ELTkSY_C70Qj8SbPh7F121Q3iA_zNlt8bS-pzOMiCBgG/agent/" + "EPkVulMF7So04EJqUDmmHu6SkllpbOt-KJOnSwckmXwz") + + holder1 = resolve_oobi(client2, "holder1", + "http://127.0.0.1:3902/oobi/ENIatcaOLTJ3AMCbv0ZiTXR-2HGrJAwsyXVKhQpwuaIq/agent/" + "ENEDfnaIJyB-ITwEZGv559Mzdk0lNng3UaQKJWzFoTK0") + resolve_oobi(client2, "issuer", + "http://127.0.0.1:3902/oobi/ELTkSY_C70Qj8SbPh7F121Q3iA_zNlt8bS-pzOMiCBgG/agent/" + "EPkVulMF7So04EJqUDmmHu6SkllpbOt-KJOnSwckmXwz") + + resolve_oobi(client0, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao") + resolve_oobi(client1, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao") + resolve_oobi(client2, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao") + + words = client1.challenges().generate() + print(f"Challenging holder1 with {words}") + client2.challenges().respond("holder2", holder1['i'], words) + + op = client1.challenges().verify("holder1", holder2['i'], words) + while not op["done"]: + op = client1.operations().get(op['name']) + sleep(1) + + exn = serdering.SerderKERI(sad=op["response"]['exn']) + print(f"Challenge signed in {exn.said}") + client1.challenges().responded("holder1", holder2['i'], exn.said) + + states = [holder1, holder2] + + member1 = get_aid(client1, "holder1") + member2 = get_aid(client2, "holder2") + + op1 = create_multisig(client1, "holder", member1, states) + op2 = create_multisig(client2, "holder", member2, states) + + gaid1 = wait_on_operation(client1, op1) + print(f"{gaid1['i']} created for holder1") + gaid2 = wait_on_operation(client2, op2) + print(f"{gaid2['i']} created for holder2") + + ghab1 = client1.identifiers().get("holder") + ghab2 = client2.identifiers().get("holder") + + stamp = helping.nowIso8601() + add_end_role_multisig(client1, "holder", ghab1, member1, client1.agent.pre, stamp=stamp) + op1 = add_end_role_multisig(client2, "holder", ghab2, member2, client1.agent.pre, stamp=stamp) + add_end_role_multisig(client1, "holder", ghab1, member1, client2.agent.pre, stamp=stamp) + op2 = add_end_role_multisig(client2, "holder", ghab2, member2, client2.agent.pre, stamp=stamp) + + while not op1["done"]: + op1 = client1.operations().get(op1['name']) + sleep(1) + + while not op2["done"]: + op2 = client1.operations().get(op2['name']) + sleep(1) + + holder = resolve_oobi(client0, "holder", "http://127.0.0.1:3902/oobi/EH_axvx0v0gwQaCawqem5u8ZeDKx9TUWKsowTa_xj0yb") + + create_credential(client0, holder) + + notificatons = client1.notifications() + + notes = notificatons.list() + while notes['total'] < 4: + sleep(1) + notes = notificatons.list() + + grant = notes['notes'][3] + gsaid = grant['a']['d'] + print(f"Received grant notification for grant {gsaid}") + + print(f"\nSending admit back") + create_admit(client1, "holder1", "holder", gsaid, [member2['prefix']]) + create_admit(client2, "holder2", "holder", gsaid, [member1['prefix']]) + + notificatons = client0.notifications() + + notes = notificatons.list() + while notes['total'] < 1: + sleep(1) + notes = notificatons.list() + + print(f"\nissuer notifications..") + print(notes) + + print(f"\nChecking credentials for holder1...") + credentials = client1.credentials().list() + while len(credentials) < 1: + print(' No credentials yet...') + sleep(1) + credentials = client1.credentials().list() + + print('holder1 recieved credential: ') + creder = serdering.SerderACDC(sad=credentials[0]['sad']) + print(creder.pretty(size=5000)) + +def create_agent(bran, controller, agent): + url = "http://localhost:3901" + tier = Tiers.low + client = SignifyClient(passcode=bran, tier=tier) + assert client.controller == controller + + evt, siger = client.ctrl.event() + + res = requests.post(url="http://localhost:3903/boot", + json=dict( + icp=evt.ked, + sig=siger.qb64, + stem=client.ctrl.stem, + pidx=1, + tier=client.ctrl.tier)) + + if res.status_code != requests.codes.accepted: + raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") + + client.connect(url=url, ) + assert client.agent is not None + print("Agent created:") + print(f" Agent: {client.agent.pre} Controller: {client.agent.delpre}") + assert client.agent.pre == agent + assert client.agent.delpre == controller + return client + + +def create_aid(client, name, bran, expected): + identifiers = client.identifiers() + (_, _, op) = identifiers.create(name, bran=bran) + icp = op["response"] + serder = serdering.SerderKERI(sad=icp) + assert serder.pre == expected + print(f"AID Created: {serder.pre}") + + +def resolve_oobi(client, alias, url): + oobis = client.oobis() + operations = client.operations() + + op = oobis.resolve(oobi=url, + alias=alias) + + print(f"resolving oobi for {alias}") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + print("... done") + return op["response"] + + +def create_multisig(client, name, member, states): + identifiers = client.identifiers() + exchanges = client.exchanges() + + icp, isigs, op = identifiers.create(name, algo=Algos.group, mhab=member, + isith=["1/2", "1/2"], nsith=["1/2", "1/2"], + states=states, + rstates=states) + + smids = [state['i'] for state in states] + recps = [x['i'] for x in states if x['i'] != member['prefix']] + + embeds = dict( + icp=eventing.messagize(serder=icp, sigers=[coring.Siger(qb64=sig) for sig in isigs]) + ) + + exchanges.send(member['name'], "multisig", sender=member, route="/multisig/icp", + payload=dict(gid=icp.pre, smids=smids, rmids=smids), + embeds=embeds, recipients=recps) + + return op + + +def create_admit(client, participant, group, said, recp): + exchanges = client.exchanges() + ipex = client.ipex() + + res = exchanges.get(said) + grant = serdering.SerderKERI(sad=res['exn']) + ghab = get_aid(client, group) + mhab = get_aid(client, participant) + + admit, sigs, end = ipex.admit(ghab, "", said, dt=TIME) + + mstate = ghab["state"] + seal = eventing.SealEvent(i=ghab["prefix"], s=mstate["ee"]["s"], d=mstate["ee"]["d"]) + ims = eventing.messagize(serder=admit, sigers=[coring.Siger(qb64=sig) for sig in sigs], seal=seal) + ims.extend(end) + embeds = dict( + exn=ims + ) + atc = bytes(ims[admit.size:]) + + exchanges.send(participant, "multisig", sender=mhab, route="/multisig/exn", + payload=dict(gid=ghab["prefix"]), + embeds=embeds, recipients=recp) + + ipex.submitAdmit(ghab['name'], exn=admit, sigs=sigs, atc=end, recp=[grant.ked['i']]) + + +def get_aid(client, name): + identifiers = client.identifiers() + return identifiers.get(name) + + +def wait_on_operation(client, op): + operations = client.operations() + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + return op["response"] + + +def add_end_role(client, name): + identifiers = client.identifiers() + identifiers.addEndRole(name, eid=client.agent.pre) + + +def add_end_role_multisig(client, name, ghab, m, eid, stamp=None): + exchanges = client.exchanges() + identifiers = client.identifiers() + + rpy, sigs, op = identifiers.addEndRole(name, eid=eid, stamp=stamp) + + gstate = ghab["state"] + seal = eventing.SealEvent(i=ghab["prefix"], s=gstate["ee"]["s"], d=gstate["ee"]["d"]) + ims = eventing.messagize(serder=rpy, sigers=[coring.Siger(qb64=sig) for sig in sigs], seal=seal) + embeds = dict( + rpy=ims + ) + + members = identifiers.members(name) + recps = [] + for member in members['signing']: + recp = member['aid'] + if recp == m['prefix']: + continue + + recps.append(recp) + + exn, _, _ = exchanges.send(m['name'], "multisig", sender=m, route="/multisig/rpy", + payload=dict(gid=ghab['prefix']), + embeds=embeds, recipients=recps) + + return op + + +def create_credential(client, holder): + registries = client.registries() + identifiers = client.identifiers() + credentials = client.credentials() + operations = client.operations() + ipex = client.ipex() + exchanges = client.exchanges() + + issuer = identifiers.get("issuer") + + print("Creating vLEI Registry") + _, _, _, op = registries.create(hab=issuer, registryName="vLEI") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + print("... created") + + issuer = identifiers.get("issuer") + registry = registries.get(name="issuer", registryName="vLEI") + data = { + "LEI": "5493001KJTIIGC8Y1R17" + } + creder, iserder, anc, sigs, op = credentials.create(issuer, registry, data=data, + schema="EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao", + recipient=holder['i']) + print(f"Creating credential {creder.said}") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + print("... created") + + prefixer = coring.Prefixer(qb64=iserder.pre) + seqner = coring.Seqner(sn=iserder.sn) + acdc = signing.serialize(creder, prefixer, seqner, coring.Saider(qb64=iserder.said)) + iss = registries.serialize(iserder, anc) + + grant, sigs, end = ipex.grant(issuer, recp=holder['i'], acdc=acdc, + iss=iss, message="", + anc=eventing.messagize(serder=anc, sigers=[coring.Siger(qb64=sig) for sig in sigs])) + print(f"Sending grant {grant.said}") + exchanges.sendFromEvents("issuer", "credential", grant, sigs, end, [holder['i']]) + print("... sent") + + +if __name__ == "__main__": + multisig_holder() diff --git a/scripts/multisig-issuer-holder.py b/scripts/multisig-issuer-holder.py new file mode 100644 index 0000000..a178f19 --- /dev/null +++ b/scripts/multisig-issuer-holder.py @@ -0,0 +1,488 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +from time import sleep + +import requests +from keri import kering +from keri.app import signing +from keri.app.keeping import Algos +from keri.core import coring, eventing, serdering +from keri.core.coring import Tiers +from keri.help import helping + +from signify.app.clienting import SignifyClient + +TIME = "2023-10-15T16:01:37.000000+00:00" + + +def multisig_holder(): + print("Creating issuer0 agent") + client0 = create_agent(b'Dmopaoe5tANSD8A5rwIhW', + "EGTZsyZyREvrD-swB4US5n-1r7h-40sVPIrmS14ixuoJ", + "EPkVulMF7So04EJqUDmmHu6SkllpbOt-KJOnSwckmXwz") + + print("\nCreating issuer0 AID") + create_aid(client0, "issuer0", "W1OnK0b5rKq6TcKBWhsQa", "ELTkSY_C70Qj8SbPh7F121Q3iA_zNlt8bS-pzOMiCBgG") + add_end_role(client0, "issuer0") + issuer0 = get_aid(client0, "issuer0") + issuer0Pre = issuer0["prefix"] + + print("\nCreating issuer1 agent") + client1 = create_agent(b'Dmopaoe5tANSD8A5rwABC', + "ECJhEr1SBaNw2MkT0_4P5VivE5Olwvmhh2XH1Q7RGiRM", + "ELe_fMsUL54-_jcDRYPa-ln_dGWFOMPt0sJyXaBf0m8G") + + print("\nCreating issuer1 AID") + create_aid(client1, "issuer1", "W1OnK0b5rKq6TcKBWhsQX", "EFEZBelGfEsFWssz28VZcNNh4bZDsm30UnvDIb3IoI2o") + add_end_role(client1, "issuer1") + issuer1 = get_aid(client1, "issuer1") + issuer1Pre = issuer1["prefix"] + + print("\nResolving Issuer participant OOBIs with each other") + issuer1 = resolve_oobi(client0, "issuer1", + "http://127.0.0.1:3902/oobi/EFEZBelGfEsFWssz28VZcNNh4bZDsm30UnvDIb3IoI2o/agent/" + "ELe_fMsUL54-_jcDRYPa-ln_dGWFOMPt0sJyXaBf0m8G") + + issuer0 = resolve_oobi(client1, "issuer0", + "http://127.0.0.1:3902/oobi/ELTkSY_C70Qj8SbPh7F121Q3iA_zNlt8bS-pzOMiCBgG/agent/" + "EPkVulMF7So04EJqUDmmHu6SkllpbOt-KJOnSwckmXwz") + + print("\nCreating Issuer Mutlsig AID") + states = [issuer0, issuer1] + + member1 = get_aid(client0, "issuer0") + member2 = get_aid(client1, "issuer1") + + op1 = create_multisig(client0, "issuer", member1, states) + op2 = create_multisig(client1, "issuer", member2, states) + + gaid1 = wait_on_operation(client0, op1) + print(f"{gaid1['i']} created for issuer0") + gaid2 = wait_on_operation(client1, op2) + print(f"{gaid2['i']} created for issuer1") + + print("\nAuthorizing agent endpoints for Issuer Multisig") + ighab1 = client0.identifiers().get("issuer") + ighab2 = client1.identifiers().get("issuer") + + stamp = helping.nowIso8601() + add_end_role_multisig(client0, "issuer", ighab1, member1, client1.agent.pre, stamp=stamp) + op1 = add_end_role_multisig(client1, "issuer", ighab2, member2, client1.agent.pre, stamp=stamp) + add_end_role_multisig(client0, "issuer", ighab1, member1, client1.agent.pre, stamp=stamp) + op2 = add_end_role_multisig(client1, "issuer", ighab2, member2, client1.agent.pre, stamp=stamp) + + while not op1["done"]: + op1 = client1.operations().get(op1['name']) + sleep(0.25) + + while not op2["done"]: + op2 = client1.operations().get(op2['name']) + sleep(0.25) + + print("\nCreating holder0 agent") + hclient0 = create_agent(b'PoLT1X6fDQliXyCuzCVuv', + "EBqP5_kfQIsBWPWSKOL0iiaDv-nwVvNsN0YHP7SYKK2u", + "ENEDfnaIJyB-ITwEZGv559Mzdk0lNng3UaQKJWzFoTK0") + + print("\nCreating holder0 AID") + create_aid(hclient0, "holder0", "B-GzoqRMFLGtV0Zy0Jajw", "ENIatcaOLTJ3AMCbv0ZiTXR-2HGrJAwsyXVKhQpwuaIq") + add_end_role(hclient0, "holder0") + holder0 = get_aid(hclient0, "holder0") + holder0Pre = holder0["prefix"] + + print("\nCreating holder1 agent") + hclient1 = create_agent(b'Pwt6yLXRSs7IjZ23tRHIV', + "EA-SUezF76zn7zF7so-T-DF8FsvI9vO1mtOhWjbdRsqK", + "EBn32S-PTYCVZWIhE4jT0l9-23suzNs2z7raYf0YpOSb") + print("\nCreating holder1 AID") + create_aid(hclient1, "holder1", "AAuXz_5CvLOXMCtZ1prCS", "EBlzZyyDM2wBzPLPKO0RiMGbYJ1PuryD1-zQOr9fKctV") + add_end_role(hclient1, "holder1") + holder1 = get_aid(hclient1, "holder1") + holder1Pre = holder1["prefix"] + + print("\nResolving Holder participant OOBIs with each other") + holder1 = resolve_oobi(hclient0, "holder1", + "http://127.0.0.1:3902/oobi/EBlzZyyDM2wBzPLPKO0RiMGbYJ1PuryD1-zQOr9fKctV/agent/" + "EBn32S-PTYCVZWIhE4jT0l9-23suzNs2z7raYf0YpOSb") + + holder0 = resolve_oobi(hclient1, "holder0", + "http://127.0.0.1:3902/oobi/ENIatcaOLTJ3AMCbv0ZiTXR-2HGrJAwsyXVKhQpwuaIq/agent/" + "ENEDfnaIJyB-ITwEZGv559Mzdk0lNng3UaQKJWzFoTK0") + + resolve_oobi(client0, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao") + resolve_oobi(client1, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao") + resolve_oobi(hclient0, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao") + resolve_oobi(hclient1, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao") + + words = hclient0.challenges().generate() + print(f"\nChallenging holder0 with {words}") + hclient1.challenges().respond("holder1", holder0['i'], words) + + op = hclient0.challenges().verify("holder0", holder1['i'], words) + while not op["done"]: + op = hclient0.operations().get(op['name']) + sleep(0.25) + + exn = serdering.SerderKERI(sad=op["response"]['exn']) + print(f"\nChallenge signed in {exn.said}") + hclient0.challenges().responded("holder0", holder1['i'], exn.said) + + states = [holder0, holder1] + + member1 = get_aid(hclient0, "holder0") + member2 = get_aid(hclient1, "holder1") + + op1 = create_multisig(hclient0, "holder", member1, states) + op2 = create_multisig(hclient1, "holder", member2, states) + + gaid1 = wait_on_operation(hclient0, op1) + print(f"{gaid1['i']} created for holder0") + gaid2 = wait_on_operation(hclient1, op2) + print(f"{gaid2['i']} created for holder1") + + ghab1 = hclient0.identifiers().get("holder") + ghab2 = hclient1.identifiers().get("holder") + + stamp = helping.nowIso8601() + add_end_role_multisig(hclient0, "holder", ghab1, member1, hclient0.agent.pre, stamp=stamp) + op1 = add_end_role_multisig(hclient1, "holder", ghab2, member2, hclient0.agent.pre, stamp=stamp) + add_end_role_multisig(hclient0, "holder", ghab1, member1, hclient1.agent.pre, stamp=stamp) + op2 = add_end_role_multisig(hclient1, "holder", ghab2, member2, hclient1.agent.pre, stamp=stamp) + + while not op1["done"]: + op1 = hclient1.operations().get(op1['name']) + sleep(0.25) + + while not op2["done"]: + op2 = hclient1.operations().get(op2['name']) + sleep(0.25) + + resolve_oobi(client0, "holder", "http://127.0.0.1:3902/oobi/EH_axvx0v0gwQaCawqem5u8ZeDKx9TUWKsowTa_xj0yb") + holder = resolve_oobi(client1, "holder", "http://127.0.0.1:3902/oobi/EH_axvx0v0gwQaCawqem5u8ZeDKx9TUWKsowTa_xj0yb") + + resolve_oobi(hclient0, "issuer", "http://127.0.0.1:3902/oobi/EHJxS_kEeS9RLZhdgHvA6imWzw4OkQ-VRNlX7HIt-9T9") + issuer = resolve_oobi(hclient0, "issuer", "http://127.0.0.1:3902/oobi/EHJxS_kEeS9RLZhdgHvA6imWzw4OkQ-VRNlX7HIt-9T9") + + print("\nCreating Credential Registry for Multisig Issuer") + op1 = create_registry(client0, "issuer0", "issuer", [issuer1Pre], "vLEI", + "AHSNDV3ABI6U8OIgKaj3aky91ZpNL54I5_7-qwtC6q2s") + op2 = create_registry(client1, "issuer1", "issuer", [issuer0Pre], "vLEI", + "AHSNDV3ABI6U8OIgKaj3aky91ZpNL54I5_7-qwtC6q2s") + while not op1["done"]: + op1 = client0.operations().get(op1['name']) + sleep(0.25) + + while not op2["done"]: + op2 = client1.operations().get(op2['name']) + sleep(0.25) + + print("\nCreating Credential from Multisig Issuer") + stamp = helping.nowIso8601() + creder, iserder, anc, sigs, op1 = create_credential(client0, "issuer0", "issuer", + [issuer1Pre], "vLEI", holder['i'], stamp) + creder, iserder, anc, sigs, op2 = create_credential(client1, "issuer1", "issuer", + [issuer0Pre], "vLEI", holder['i'], stamp) + while not op1["done"]: + op1 = client0.operations().get(op1['name']) + sleep(0.25) + + while not op2["done"]: + op2 = client1.operations().get(op2['name']) + sleep(0.25) + + print("\nSend GRANT from Multisig Issuer to Multisig Holder") + op1 = create_grant(client0, "issuer0", "issuer", creder, iserder, anc, sigs, [issuer1Pre], holder['i'], stamp) + op2 = create_grant(client1, "issuer1", "issuer", creder, iserder, anc, sigs, [issuer0Pre], holder['i'], stamp) + while not op1["done"]: + op1 = client0.operations().get(op1['name']) + sleep(0.25) + + while not op2["done"]: + op2 = client1.operations().get(op2['name']) + sleep(0.25) + + notificatons = hclient0.notifications() + + notes = notificatons.list() + while notes['total'] < 4: + sleep(3) + notes = notificatons.list() + + grant = notes['notes'][-1] + gsaid = grant['a']['d'] + print(f"Received grant notification for grant {gsaid}") + + print(f"\nSending admit back") + create_admit(hclient0, "holder0", "holder", gsaid, [holder1Pre], stamp) + create_admit(hclient1, "holder1", "holder", gsaid, [holder0Pre], stamp) + + notificatons = client0.notifications() + + notes = notificatons.list() + while notes['total'] < 1: + sleep(1) + notes = notificatons.list() + + print(f"\nChecking credentials for holder0...") + credentials = hclient0.credentials().list() + while len(credentials) < 1: + print(' No credentials yet...') + sleep(1) + credentials = hclient0.credentials().list() + + print('holder0 recieved credential: ') + creder = serdering.SerderACDC(sad=credentials[0]['sad']) + print(creder.pretty(size=5000)) + + +def create_agent(bran, controller, agent): + url = "http://localhost:3901" + tier = Tiers.low + client = SignifyClient(passcode=bran, tier=tier) + assert client.controller == controller, f"not {client.controller}" + + evt, siger = client.ctrl.event() + + res = requests.post(url="http://localhost:3903/boot", + json=dict( + icp=evt.ked, + sig=siger.qb64, + stem=client.ctrl.stem, + pidx=1, + tier=client.ctrl.tier)) + + if res.status_code != requests.codes.accepted: + raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") + + client.connect(url=url, ) + assert client.agent is not None + print("Agent created:") + print(f" Agent: {client.agent.pre} Controller: {client.agent.delpre}") + assert client.agent.pre == agent, f"not {client.agent.pre}" + assert client.agent.delpre == controller + return client + + +def create_aid(client, name, bran, expected): + identifiers = client.identifiers() + (_, _, op) = identifiers.create(name, bran=bran) + icp = op["response"] + serder = serdering.SerderKERI(sad=icp) + assert serder.pre == expected, f"not {serder.pre}" + print(f"AID Created: {serder.pre}") + + +def resolve_oobi(client, alias, url): + oobis = client.oobis() + operations = client.operations() + + op = oobis.resolve(oobi=url, + alias=alias) + + print(f"resolving oobi for {alias}") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + print("... done") + return op["response"] + + +def create_multisig(client, name, member, states): + identifiers = client.identifiers() + exchanges = client.exchanges() + + icp, isigs, op = identifiers.create(name, algo=Algos.group, mhab=member, + isith=["1/2", "1/2"], nsith=["1/2", "1/2"], + states=states, + rstates=states) + + smids = [state['i'] for state in states] + recps = [x['i'] for x in states if x['i'] != member['prefix']] + + embeds = dict( + icp=eventing.messagize(serder=icp, sigers=[coring.Siger(qb64=sig) for sig in isigs]) + ) + + exchanges.send(member['name'], "multisig", sender=member, route="/multisig/icp", + payload=dict(gid=icp.pre, smids=smids, rmids=smids), + embeds=embeds, recipients=recps) + + return op + + +def create_admit(client, participant, group, said, recp, stamp): + exchanges = client.exchanges() + ipex = client.ipex() + + res = exchanges.get(said) + grant = serdering.SerderKERI(sad=res['exn']) + ghab = get_aid(client, group) + mhab = get_aid(client, participant) + + admit, sigs, end = ipex.admit(ghab, "", said, dt=stamp) + + print(f"created ADMIT {admit.said}") + mstate = ghab["state"] + seal = eventing.SealEvent(i=ghab["prefix"], s=mstate["ee"]["s"], d=mstate["ee"]["d"]) + ims = eventing.messagize(serder=admit, sigers=[coring.Siger(qb64=sig) for sig in sigs], seal=seal) + ims.extend(end) + embeds = dict( + exn=ims + ) + + exn, gsigs, end = exchanges.createExchangeMessage(sender=mhab, route="/multisig/exn", + payload=dict(gid=ghab["prefix"]), + embeds=embeds) + + ipex.submitAdmit(ghab['name'], exn=exn, sigs=gsigs, atc=end, recp=recp) + + +def get_aid(client, name): + identifiers = client.identifiers() + return identifiers.get(name) + + +def wait_on_operation(client, op): + operations = client.operations() + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + return op["response"] + + +def add_end_role(client, name): + identifiers = client.identifiers() + identifiers.addEndRole(name, eid=client.agent.pre) + + +def add_end_role_multisig(client, name, ghab, m, eid, stamp=None): + exchanges = client.exchanges() + identifiers = client.identifiers() + + rpy, sigs, op = identifiers.addEndRole(name, eid=eid, stamp=stamp) + + gstate = ghab["state"] + seal = eventing.SealEvent(i=ghab["prefix"], s=gstate["ee"]["s"], d=gstate["ee"]["d"]) + ims = eventing.messagize(serder=rpy, sigers=[coring.Siger(qb64=sig) for sig in sigs], seal=seal) + embeds = dict( + rpy=ims + ) + + members = identifiers.members(name) + recps = [] + for member in members['signing']: + recp = member['aid'] + if recp == m['prefix']: + continue + + recps.append(recp) + + exn, _, _ = exchanges.send(m['name'], "multisig", sender=m, route="/multisig/rpy", + payload=dict(gid=ghab['prefix']), + embeds=embeds, recipients=recps) + + return op + + +def create_registry(client, localName, groupName, recp, name, nonce): + registries = client.registries() + identifiers = client.identifiers() + exchanges = client.exchanges() + + group = identifiers.get(groupName) + local = identifiers.get(localName) + + print("Creating vLEI Registry") + vcp, anc, rsigs, op = registries.create(hab=group, registryName=name, + nonce=nonce) + + embeds = dict( + vcp=vcp.raw, + anc=eventing.messagize(serder=anc, sigers=[coring.Siger(qb64=sig) for sig in rsigs]) + ) + + exchanges.send(localName, groupName, sender=local, route="/multisig/vcp", + payload=dict(gid=group["prefix"], usage="Issue vLEIs"), + embeds=embeds, recipients=recp) + + return op + + +def create_credential(client, localName, groupName, recp, registryName, holder, stamp): + registries = client.registries() + identifiers = client.identifiers() + credentials = client.credentials() + exchanges = client.exchanges() + + issuer = identifiers.get(groupName) + local = identifiers.get(localName) + + registry = registries.get(name=groupName, registryName=registryName) + data = { + "LEI": "5493001KJTIIGC8Y1R17" + } + creder, iserder, anc, sigs, op = credentials.create(issuer, registry, data=data, + schema="EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao", + recipient=holder, timestamp=stamp) + print(f"Creating credential {creder.said}") + prefixer = coring.Prefixer(qb64=iserder.pre) + seqner = coring.Seqner(sn=iserder.sn) + acdc = signing.serialize(creder, prefixer, seqner, coring.Saider(qb64=iserder.said)) + iss = registries.serialize(iserder, anc) + + embeds = dict( + acdc=acdc, + iss=iss, + anc=eventing.messagize(serder=anc, sigers=[coring.Siger(qb64=sig) for sig in sigs]) + ) + exchanges.send(localName, groupName, sender=local, route="/multisig/iss", + payload=dict(gid=issuer["prefix"]), + embeds=embeds, recipients=recp) + + return creder, iserder, anc, sigs, op + + +def create_grant(client, localName, groupName, creder, iserder, anc, sigs, recp, holder, stamp): + identifiers = client.identifiers() + ipex = client.ipex() + registries = client.registries() + exchanges = client.exchanges() + + prefixer = coring.Prefixer(qb64=iserder.pre) + seqner = coring.Seqner(sn=iserder.sn) + acdc = signing.serialize(creder, prefixer, seqner, coring.Saider(qb64=iserder.said)) + iss = registries.serialize(iserder, anc) + + issuer = identifiers.get(groupName) + local = identifiers.get(localName) + + grant, sigs, end = ipex.grant(issuer, recp=holder, acdc=acdc, + iss=iss, message="", dt=stamp, + anc=eventing.messagize(serder=anc, sigers=[coring.Siger(qb64=sig) for sig in sigs])) + + print(f'created grant {grant.said}') + mstate = issuer["state"] + seal = eventing.SealEvent(i=issuer["prefix"], s=mstate["ee"]["s"], d=mstate["ee"]["d"]) + ims = eventing.messagize(serder=grant, sigers=[coring.Siger(qb64=sig) for sig in sigs], seal=seal) + ims.extend(end.encode("utf-8")) + embeds = dict( + exn=ims + ) + + exn, gsigs, end = exchanges.createExchangeMessage(sender=local, route="/multisig/exn", + payload=dict(gid=issuer["prefix"]), + embeds=embeds) + + op = ipex.submitGrant(issuer['name'], exn=exn, sigs=gsigs, atc=end, recp=recp) + return op + + +if __name__ == "__main__": + multisig_holder() diff --git a/scripts/multisig-kli-rotation.py b/scripts/multisig-kli-rotation.py new file mode 100644 index 0000000..9ffdf9b --- /dev/null +++ b/scripts/multisig-kli-rotation.py @@ -0,0 +1,186 @@ +from time import sleep + +import requests +from keri import kering +from keri.app.keeping import Algos +from keri.core import coring, eventing +from keri.core.coring import Tiers +from signify.app.clienting import SignifyClient + + +def create_multisig(): + url = "http://localhost:3901" + bran = b'0123456789abcdefghijk' + tier = Tiers.low + + client = SignifyClient(passcode=bran, tier=tier) + print(client.controller) + assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" + + evt, siger = client.ctrl.event() + res = requests.post(url="http://localhost:3903/boot", + json=dict( + icp=evt.ked, + sig=siger.qb64, + stem=client.ctrl.stem, + pidx=1, + tier=client.ctrl.tier)) + + client.connect(url=url) + assert client.agent is not None + assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" + assert client.agent.pre == "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" + # assert client.ctrl.ridx == 0 + + if res.status_code != requests.codes.accepted: + raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") + + identifiers = client.identifiers() + operations = client.operations() + oobis = client.oobis() + exchanges = client.exchanges() + + (_, _, op) = identifiers.create("multisig3", bran="0123456789lmnopqrstuv") + icp = op["response"] + serder = serdering.SerderKERI(sad=icp) + assert serder.pre == "EOGvmhJDBbJP4zeXaRun5vSz0O3_1zB10DwNMyjXlJEv" + print(f"created AID {serder.pre}") + + identifiers.addEndRole("multisig3", eid=client.agent.pre) + + print(f"OOBI for {serder.pre}:") + oobi = oobis.get("multisig3") + print(oobi) + + op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4/witness", + alias="multisig1") + + print("resolving oobi for multisig1") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + print("... done") + + multisig1 = op["response"] + print("resolving oobi for multisig2") + op = oobis.resolve(oobi="http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1/witness", + alias="multisig2") + print("... done") + + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + multisig2 = op["response"] + + m3 = identifiers.get("multisig3") + agent0 = m3["state"] + print(f"agent is {agent0}") + + states = rstates = [multisig2, multisig1, agent0] + + icp, isigs, op = identifiers.create("multisig", algo=Algos.group, mhab=m3, + isith=["1/3", "1/3", "1/3"], nsith=["1/3", "1/3", "1/3"], + toad=3, + wits=[ + "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", + "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", + "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" + ], + states=states, + rstates=rstates) + + smids = [state['i'] for state in states] + recp = [state['i'] for state in [multisig2, multisig1]] + + embeds = dict( + icp=eventing.messagize(serder=icp, sigers=[coring.Siger(qb64=sig) for sig in isigs]) + ) + + exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/icp", + payload=dict(gid=icp.pre, smids=smids, rmids=smids), + embeds=embeds, recipients=recp) + + print("waiting on multisig creation...") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + gid = op["response"] + print(f"group multisig created {gid}") + + # Join an interaction event with the group + data = {"i": "EE77q3_zWb5ojgJr-R1vzsL5yiL4Nzm-bfSOQzQl02dy"} + ixn, xsigs, op = identifiers.interact("multisig", data=data) + + embeds = dict( + ixn=eventing.messagize(serder=ixn, sigers=[coring.Siger(qb64=sig) for sig in xsigs]) + ) + + exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/ixn", + payload=dict(gid=icp.pre, smids=smids), + embeds=embeds, recipients=recp) + + print("waiting for ixn to finish...") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + ixn = serdering.SerderKERI(sad=op["response"]) + events = client.keyEvents() + log = events.get(pre=ixn.pre) + assert len(log) == 2 + + for event in log: + print(serdering.SerderKERI(sad=event).pretty()) + + (_, _, op2) = identifiers.rotate("multisig3") + rot = op2["response"] + serder = serdering.SerderKERI(sad=rot) + print(f"rotated multisig3 to {serder.sn}") + + input("hit any key when other two participants have rotated their AIDs") + + m3 = identifiers.get("multisig3") + agent0 = m3["state"] + + keyState = client.keyStates() + op = keyState.query(pre="EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1", sn=1) + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + multisig2 = op["response"] + print(f"using key {multisig2['k'][0]}") + print(f"using dig {multisig2['n'][0]}") + + op = keyState.query(pre="EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4", sn=1) + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + multisig1 = op["response"] + print(f"using key {multisig1['k'][0]}") + print(f"using dig {multisig1['n'][0]}") + + states = rstates = [multisig1, multisig2, agent0] + + rot, rsigs, op = identifiers.rotate("multisig", states=states, rstates=rstates) + embeds = dict( + rot=eventing.messagize(serder=rot, sigers=[coring.Siger(qb64=sig) for sig in rsigs]) + ) + + smids = [state['i'] for state in states] + recp = [state['i'] for state in [multisig1, multisig2]] + + rexn, _, _ = exchanges.send("multisig3", "multisig", sender=m3, route="/multisig/rot", + payload=dict(gid=icp.pre, smids=smids, rmids=smids), + embeds=embeds, recipients=recp) + + print(rexn.pretty(size=5000)) + print("Waiting for multisig rotation...") + while not op["done"]: + op = operations.get(op["name"]) + sleep(1) + + +if __name__ == "__main__": + create_multisig() diff --git a/scripts/send_admit.py b/scripts/send_admit.py new file mode 100644 index 0000000..65214ed --- /dev/null +++ b/scripts/send_admit.py @@ -0,0 +1,36 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +import sys + +from keri.core.coring import Tiers + +from signify.app.clienting import SignifyClient + + +def send_admit(grant, recp): + url = "http://localhost:3901" + bran = b'0123456789abcdefghijk' + tier = Tiers.low + + client = SignifyClient(passcode=bran, tier=tier, url=url) + + identifiers = client.identifiers() + + hab = identifiers.get("BankUser") + create_admit(client, hab, grant, recp) + + +def create_admit(client, hab, said, recp, dt=None): + ipex = client.ipex() + admit, sigs, atc = ipex.admit(hab, "", said, dt=dt) + + ipex.submitAdmit(hab['name'], exn=admit, sigs=sigs, atc=atc, recp=recp) + + +if __name__ == "__main__": + send_admit(sys.argv[1], sys.argv[2]) diff --git a/scripts/single-issuer-holder.py b/scripts/single-issuer-holder.py new file mode 100644 index 0000000..c140ee3 --- /dev/null +++ b/scripts/single-issuer-holder.py @@ -0,0 +1,191 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.clienting module + +Testing clienting with integration tests that require a running KERIA Cloud Agent +""" +from time import sleep +from requests import post +from pysodium import randombytes, crypto_sign_SEEDBYTES +from keri.app import signing +from keri.core import coring, eventing, serdering +from keri.core.coring import Tiers +from keri.help import helping +from signify.app.clienting import SignifyClient + +URL = 'http://127.0.0.1:3901' +BOOT_URL = 'http://127.0.0.1:3903' +SCHEMA_SAID = 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao' +WITNESS_AIDS = [] +SCHEMA_OOBI = 'http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao' + + +def random_passcode(): + return coring.Salter(raw=randombytes(crypto_sign_SEEDBYTES)).qb64 + + +def create_timestamp(): + return helping.nowIso8601() + + +def connect(): + client = SignifyClient(passcode=random_passcode(), tier=Tiers.low) + + evt, siger = client.ctrl.event() + post(url=BOOT_URL + "/boot", + json=dict( + icp=evt.ked, + sig=siger.qb64, + stem=client.ctrl.stem, + pidx=1, + tier=client.ctrl.tier)) + + client.connect(url=URL) + + return client + + +def create_identifier(client: SignifyClient, name: str): + result = client.identifiers().create(name, toad=str(len(WITNESS_AIDS)), wits=WITNESS_AIDS) + op = result[2] + + while not op["done"]: + op = client.operations().get(op["name"]) + sleep(1) + + hab = client.identifiers().get(name) + + client.identifiers().addEndRole(name=name, eid=client.agent.pre) + + return hab["prefix"] + + +def get_agent_oobi(client: SignifyClient, name: str): + result = client.oobis().get(name, role='agent') + return result["oobis"][0] + + +def resolve_oobi(client: SignifyClient, oobi: str, alias: str): + op = client.oobis().resolve(oobi, alias) + while not op['done']: + op = client.operations().get(op["name"]) + sleep(1) + + +def create_registry(client: SignifyClient, name: str, registry_name: str): + hab = client.identifiers().get(name) + result = client.registries().create(hab=hab, registryName=registry_name) + op = result[3] + while not op['done']: + op = client.operations().get(op["name"]) + sleep(1) + + registry = client.registries().get(name=name, registryName=registry_name) + return registry + + +def issue_credential(client: SignifyClient, name: str, registry_name: str, schema: str, recipient: str, data): + hab = client.identifiers().get(name) + registry = client.registries().get(name, registryName=registry_name) + creder, iserder, anc, sigs, op = client.credentials().create(hab, registry=registry, data=data, schema=schema, + recipient=recipient, timestamp=create_timestamp()) + + while not op['done']: + print(f"Waiting for creds... {op['name']}") + op = client.operations().get(op['name']) + sleep(1) + + prefixer = coring.Prefixer(qb64=iserder.pre) + seqner = coring.Seqner(sn=iserder.sn) + acdc = signing.serialize(creder, prefixer, seqner, coring.Saider(qb64=iserder.said)) + iss = client.registries().serialize(iserder, anc) + + grant, sigs, end = client.ipex().grant(hab, recp=recipient, acdc=acdc, + iss=iss, message="", + anc=eventing.messagize(serder=anc, + sigers=[coring.Siger(qb64=sig) for sig in sigs]), + dt=create_timestamp()) + + client.exchanges().sendFromEvents(name=name, topic="credential", exn=grant, sigs=sigs, atc=end, + recipients=[recipient]) + + return + + +def wait_for_notification(client: SignifyClient, route: str): + while True: + notifications = client.notifications().list() + for notif in notifications['notes']: + if notif['a']['r'] == route: + return notif + sleep(1) + + +def admit_credential(client: SignifyClient, name: str, said: str, recipient: str): + dt = create_timestamp() + hab = client.identifiers().get(name) + + admit, sigs, end = client.ipex().admit(hab, '', said, dt) + + client.ipex().submitAdmit(name=name, exn=admit, sigs=sigs, atc=end, recp=recipient) + + +def run(): + issuer_client = connect() + holder_client = connect() + + print(f"Holder connected agent {holder_client.agent.pre}") + print(f"Issuer connected agent {issuer_client.agent.pre}") + + issuer_prefix = create_identifier(issuer_client, "issuer") + holder_prefix = create_identifier(holder_client, "holder") + + print(f"Issuer prefix {issuer_prefix}") + print(f"Holder prefix {holder_prefix}") + + issuer_oobi = get_agent_oobi(issuer_client, 'issuer') + holder_oobi = get_agent_oobi(holder_client, 'holder') + resolve_oobi(issuer_client, holder_oobi, 'holder') + resolve_oobi(issuer_client, SCHEMA_OOBI, 'schema') + resolve_oobi(holder_client, issuer_oobi, 'issuer') + resolve_oobi(holder_client, SCHEMA_OOBI, 'schema') + + registry = create_registry(issuer_client, 'issuer', 'vLEI') + print(f"Registry created name={registry['name']}, regk={registry['regk']}") + + data = { + "LEI": "5493001KJTIIGC8Y1R17" + } + + issue_credential( + client=issuer_client, + name='issuer', + registry_name='vLEI', + schema=SCHEMA_SAID, + recipient=holder_prefix, + data=data) + + notification = wait_for_notification(holder_client, '/exn/ipex/grant') + print(f"Received grant {notification}") + + admit_credential(holder_client, 'holder', notification['a']['d'], issuer_prefix) + + holder_client.notifications().markAsRead(notification['i']) + print(f"Notification marked!") + + print(f"Listing credentials...") + + credentials = holder_client.credentials().list(filtr={'-a-i': holder_prefix}) + while len(credentials) < 1: + print('No credentials yet...') + sleep(1) + credentials = holder_client.credentials().list(filtr={'-a-i': holder_prefix}) + + print('Succeeded') + creder = serdering.SerderACDC(sad=credentials[0]['sad']) + print(creder.pretty(size=5000)) + + +if __name__ == "__main__": + run() diff --git a/setup.py b/setup.py index fb6680b..d30fd11 100644 --- a/setup.py +++ b/setup.py @@ -11,14 +11,14 @@ $ pip3 install twine $ python3 setup.py sdist -$ twine upload dist/keri-0.0.1.tar.gz +$ twine upload dist/signifypy-0.0.1.tar.gz Create release git: -$ git tag -a v0.4.2 -m "bump version"x +$ git tag -a v0.4.2 -m "bump version" $ git push --tags $ git checkout -b release_0.4.2 $ git push --set-upstream origin release_0.4.2 -$ git checkout master +$ git checkout main Best practices for setup.py and requirements.txt https://caremad.io/posts/2013/07/setup-vs-requirement/ @@ -31,7 +31,7 @@ from setuptools import find_packages, setup setup( name='signifypy', - version='0.0.1', # also change in src/signify/__init__.py + version='0.1.0', # also change in src/signify/__init__.py license='Apache Software License 2.0', description='Signify', long_description="KERI Signing at the Edge Infrastructure", @@ -53,25 +53,19 @@ 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: CPython', - # uncomment if you test on these interpreters: - # 'Programming Language :: Python :: Implementation :: PyPy', - # 'Programming Language :: Python :: Implementation :: IronPython', - # 'Programming Language :: Python :: Implementation :: Jython', - # 'Programming Language :: Python :: Implementation :: Stackless', 'Topic :: Utilities', ], project_urls={ - 'Documentation': 'https://signify.readthedocs.io/', - 'Changelog': 'https://signify.readthedocs.io/en/latest/changelog.html', - 'Issue Tracker': 'https://github.com/WebOfTrust/signify/issues', + 'Documentation': 'https://signifypy.readthedocs.io/', + 'Changelog': 'https://signifypy.readthedocs.io/en/latest/changelog.html', + 'Issue Tracker': 'https://github.com/WebOfTrust/signifypy/issues', }, keywords=[ # eg: 'keyword1', 'keyword2', 'keyword3', ], python_requires='>=3.10.4', install_requires=[ - 'keri>=1.0.0', - 'keria>=0.0.1', + 'keri>=1.1.0', 'multicommand>=1.0.0', 'requests>=2.28', 'http_sfv>=0.9.8', @@ -85,12 +79,13 @@ 'responses>=0.22.0', 'coverage>=6.5.0', 'pytest>=7.2.0', + 'mockito>=1.4.0' ], setup_requires=[ ], entry_points={ 'console_scripts': [ - 'signify = signify.app.cli.signify:main', + 'sigpy = signify.app.cli.sigpy:main', ] }, ) diff --git a/src/signify/__init__.py b/src/signify/__init__.py index 13aec95..e39210a 100644 --- a/src/signify/__init__.py +++ b/src/signify/__init__.py @@ -3,5 +3,5 @@ main package """ -__version__ = '0.0.1' # also change in setup.py +__version__ = '0.1.0' # also change in setup.py diff --git a/src/signify/app/aiding.py b/src/signify/app/aiding.py index 9d40a02..7fd8d76 100644 --- a/src/signify/app/aiding.py +++ b/src/signify/app/aiding.py @@ -11,7 +11,6 @@ from keri.core import eventing from keri.core.coring import MtrDex, Tholder from keri.kering import Roles -from requests import exceptions from signify.app.clienting import SignifyClient from signify.core import httping @@ -28,7 +27,7 @@ def list(self, start=0, end=24): res = self.client.get(f"/identifiers", headers=headers) cr = res.headers["content-range"] - start, end, total = httping.parseRangeHeader(cr) + start, end, total = httping.parseRangeHeader(cr, "aids") return dict(start=start, end=end, total=total, aids=res.json()) @@ -37,7 +36,7 @@ def get(self, name): return res.json() def create(self, name, transferable=True, isith="1", nsith="1", wits=None, toad="0", proxy=None, delpre=None, - dcode=MtrDex.Blake3_256, data=None, algo=Algos.salty, **kwargs): + dcode=MtrDex.Blake3_256, data=None, algo=Algos.salty, estOnly=False, DnD=False, **kwargs): # Get the algo specific key params keeper = self.client.manager.new(algo, self.client.pidx, **kwargs) @@ -46,6 +45,12 @@ def create(self, name, transferable=True, isith="1", nsith="1", wits=None, toad= wits = wits if wits is not None else [] data = [data] if data is not None else [] + cnfg = [] + if estOnly: + cnfg.append(eventing.TraitCodex.EstOnly) + if DnD: + cnfg.append(eventing.TraitCodex.DoNotDelegate) + if delpre is not None: serder = eventing.delcept(delpre=delpre, keys=keys, @@ -55,6 +60,7 @@ def create(self, name, transferable=True, isith="1", nsith="1", wits=None, toad= code=dcode, wits=wits, toad=toad, + cnfg=cnfg, data=data) else: serder = eventing.incept(keys=keys, @@ -64,6 +70,7 @@ def create(self, name, transferable=True, isith="1", nsith="1", wits=None, toad= code=dcode, wits=wits, toad=toad, + cnfg=cnfg, data=data) sigs = keeper.sign(serder.raw) @@ -84,7 +91,7 @@ def create(self, name, transferable=True, isith="1", nsith="1", wits=None, toad= self.client.pidx = self.client.pidx + 1 res = self.client.post("/identifiers", json=json) - return res.json() + return serder, sigs, res.json() def update(self, name, typ, **kwas): if typ == "interact": @@ -119,7 +126,7 @@ def interact(self, name, data=None): json[keeper.algo] = keeper.params() res = self.client.put(f"/identifiers/{name}?type=ixn", json=json) - return res.json() + return serder, sigs, res.json() def rotate(self, name, *, transferable=True, nsith=None, toad=None, cuts=None, adds=None, data=None, ncode=MtrDex.Ed25519_Seed, ncount=1, ncodes=None, states=None, rstates=None): @@ -131,7 +138,7 @@ def rotate(self, name, *, transferable=True, nsith=None, toad=None, cuts=None, a dig = state["d"] ridx = int(state["s"], 16) + 1 wits = state['b'] - isith = state["kt"] + isith = state["kt"] if "kt" in state else None if nsith is None: nsith = isith # use new current as default @@ -182,7 +189,7 @@ def rotate(self, name, *, transferable=True, nsith=None, toad=None, cuts=None, a json['rmids'] = [state['i'] for state in rstates] res = self.client.put(f"/identifiers/{name}", json=json) - return res.json() + return serder, sigs, res.json() def addEndRole(self, name, *, role=Roles.agent, eid=None, stamp=None): hab = self.get(name) @@ -197,12 +204,12 @@ def addEndRole(self, name, *, role=Roles.agent, eid=None, stamp=None): ) res = self.client.post(f"/identifiers/{name}/endroles", json=json) - return res.json() + return rpy, sigs, res.json() def sign(self, name, ser): hab = self.get(name) keeper = self.client.manager.get(aid=hab) - sigs = keeper.sign(ser=ser) + sigs = keeper.sign(ser=ser.raw) return sigs diff --git a/src/signify/app/challenging.py b/src/signify/app/challenging.py new file mode 100644 index 0000000..972b22c --- /dev/null +++ b/src/signify/app/challenging.py @@ -0,0 +1,77 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.challenging module + +""" +from signify.app.clienting import SignifyClient + + +class Challenges: + """ Challenges domain object """ + + def __init__(self, client: SignifyClient): + """ Create domain class for working with credentials for a single AID + + Parameters: + client (SignifyClient): Signify client class for access resources on a KERIA service instance + + """ + self.client = client + + def generate(self): + """ Request 12 random word challenge phrase from server + + Returns: + list: array of 12 random words + + """ + + res = self.client.get("/challenges") + resp = res.json() + return resp["words"] + + def respond(self, name, recp, words): + hab = self.client.identifiers().get(name) + exchanges = self.client.exchanges() + + _, _, res = exchanges.send(name, "challenge", sender=hab, route="/challenge/response", + payload=dict(words=words), + embeds=dict(), recipients=[recp]) + + return res + + def verify(self, name, source, words): + """ Ask Agent to verify a given sender signed the provided words + + Parameters: + name (str): human readable name of AID environment + source(str): qb64 AID of source of challenge response to check for + words(list): list of challenge words to check for + """ + + json = dict( + words=words + ) + + res = self.client.post(f"/challenges/{name}/verify/{source}", json=json) + return res.json() + + def responded(self, name, source, said): + """ Mark challenge response as signed and accepted + + Parameters: + name (str): human readable name of AID environment + source (str): qb64 AID of signer + said (str): qb64 AID of exn message representing the signed response + + Returns: + bool: True means successful + + """ + json = dict( + said=said + ) + + self.client.put(f"/challenges/{name}/verify/{source}", json=json) + return True diff --git a/integration/app/__init__.py b/src/signify/app/cli/__init__.py similarity index 100% rename from integration/app/__init__.py rename to src/signify/app/cli/__init__.py diff --git a/src/signify/app/cli/commands/__init__.py b/src/signify/app/cli/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/signify/app/cli/commands/status.py b/src/signify/app/cli/commands/status.py new file mode 100644 index 0000000..d14fe96 --- /dev/null +++ b/src/signify/app/cli/commands/status.py @@ -0,0 +1,92 @@ +# -*- encoding: utf-8 -*- +""" +KERI +keri.kli.commands module + +""" +import argparse +import sys + +from hio import help +from hio.base import doing +from keri.app.cli.common import terming +from keri.core.coring import Tiers + +from signify.app import clienting + +logger = help.ogler.getLogger() + +parser = argparse.ArgumentParser(description='View status of a local AID') +parser.set_defaults(handler=lambda args: handler(args), + transferable=True) +parser.add_argument('--url', '-u', help='Agent URL, defaults to "http://localhost:3901"', + default="http://localhost:3901") +parser.add_argument('--alias', '-a', help='human readable alias for the new identifier prefix', default=None) +parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)', + dest="bran", default=None) # passcode => bran + +parser.add_argument("--verbose", "-V", help="print JSON of all current events", action="store_true") + + +def handler(args): + kwa = dict(args=args) + return [doing.doify(status, **kwa)] + + +def status(tymth, tock=0.0, **opts): + """ Command line status handler + + """ + _ = (yield tock) + args = opts["args"] + alias = args.alias + bran = args.bran + + url = args.url + tier = Tiers.low + + client = clienting.SignifyClient(passcode=bran, tier=tier, url=url) + identifiers = client.identifiers() + + aid = identifiers.get(alias) + + printIdentifier(aid) + + +def printIdentifier(aid, label="Identifier"): + + state = aid["state"] + + print(f"Alias: \t{aid['name']}") + print("{}: {}".format(label, aid["prefix"])) + print("Seq No:\t{}".format(state['s'])) + if state['di']: + anchor = True + print("Delegated Identifier") + sys.stdout.write(f" Delegator: {state['di']} ") + if anchor: + print(f"{terming.Colors.OKGREEN}{terming.Symbols.CHECKMARK} Anchored{terming.Colors.ENDC}") + else: + print(f"{terming.Colors.FAIL}{terming.Symbols.FAILED} Not Anchored{terming.Colors.ENDC}") + print() + + if "group" in aid: + group = aid["group"] + accepted = True + print("Group Identifier") + sys.stdout.write(f" Local Indentifier: {group['mhab']['prefix']} ") + if accepted: + print(f"{terming.Colors.OKGREEN}{terming.Symbols.CHECKMARK} Fully Signed{terming.Colors.ENDC}") + else: + print(f"{terming.Colors.FAIL}{terming.Symbols.FAILED} Not Fully Signed{terming.Colors.ENDC}") + + print("\nWitnesses:") + print("Count:\t\t{}".format(len(state['b']))) + print("Receipts:\t{}".format(len(aid['windexes']))) + print("Threshold:\t{}".format(state['bt'])) + print("\nPublic Keys:\t") + for idx, key in enumerate(state['k']): + print(f'\t{idx+1}. {key}') + + print() + diff --git a/src/signify/app/cli/sigpy.py b/src/signify/app/cli/sigpy.py new file mode 100644 index 0000000..6e02514 --- /dev/null +++ b/src/signify/app/cli/sigpy.py @@ -0,0 +1,38 @@ +# -*- encoding: utf-8 -*- +""" +keri.kli.commands module + +""" +import multicommand +from keri import help + +from keri.app import directing +from signify.app.cli import commands + +logger = help.ogler.getLogger() + + +def main(): + parser = multicommand.create_parser(commands) + args = parser.parse_args() + + if not hasattr(args, 'handler'): + parser.print_help() + return + + try: + doers = args.handler(args) + directing.runController(doers=doers, expire=0.0) + + except Exception as ex: + import os + if os.getenv('DEBUG_SIGPY'): + import traceback + traceback.print_exc() + else: + print(f"ERR: {ex}") + return -1 + + +if __name__ == "__main__": + main() diff --git a/src/signify/app/clienting.py b/src/signify/app/clienting.py index f4f7064..d51eea9 100644 --- a/src/signify/app/clienting.py +++ b/src/signify/app/clienting.py @@ -1,6 +1,6 @@ # -*- encoding: utf-8 -*- """ -KERI +Signify signify.app.clienting module """ @@ -15,16 +15,8 @@ from requests import HTTPError from requests.auth import AuthBase -from signify.core import keeping -from signify.core.authing import Authenticater, Controller, Agent - - -@dataclass -class State: - controller: dict = None - agent : dict = None - ridx: int = None - pidx: int = None +from signify.core import keeping, authing +from signify.signifying import State class SignifyClient: @@ -39,14 +31,13 @@ def __init__(self, passcode, url=None, tier=Tiers.low, extern_modules=None): self.tier = tier self.extern_modules = extern_modules - self.ctrl = None self.mgr = None self.session = None self.agent = None self.authn = None self.base = None - self.ctrl = Controller(bran=self.bran, tier=self.tier) + self.ctrl = authing.Controller(bran=self.bran, tier=self.tier) if url is not None: self.connect(url) @@ -62,10 +53,10 @@ def connect(self, url): self.pidx = state.pidx # Create agent representing the AID of the cloud agent - self.agent = Agent(state=state.agent) + self.agent = authing.Agent(state=state.agent) # Create controller representing local auth AID - self.ctrl = Controller(bran=self.bran, tier=self.tier, state=state.controller) + self.ctrl = authing.Controller(bran=self.bran, tier=self.tier, state=state.controller) self.mgr = keeping.Manager(salter=self.ctrl.salter, extern_modules=self.extern_modules) if self.agent.delpre != self.ctrl.pre: @@ -74,7 +65,7 @@ def connect(self, url): if self.ctrl.serder.sn == 0: self.approveDelegation() - self.authn = Authenticater(agent=self.agent, ctrl=self.ctrl) + self.authn = authing.Authenticater(agent=self.agent, ctrl=self.ctrl) self.session.auth = SignifyAuth(self.authn) self.session.hooks = dict(response=self.authn.verify) @@ -244,6 +235,34 @@ def endroles(self): from signify.app.ending import EndRoleAuthorizations return EndRoleAuthorizations(client=self) + def notifications(self): + from signify.app.notifying import Notifications + return Notifications(client=self) + + def groups(self): + from signify.app.grouping import Groups + return Groups(client=self) + + def registries(self): + from signify.app.credentialing import Registries + return Registries(client=self) + + def exchanges(self): + from signify.peer.exchanging import Exchanges + return Exchanges(client=self) + + def ipex(self): + from signify.app.credentialing import Ipex + return Ipex(client=self) + + def challenges(self): + from signify.app.challenging import Challenges + return Challenges(client=self) + + def contacts(self): + from signify.app.contacting import Contacts + return Contacts(client=self) + @staticmethod def raiseForStatus(res): try: diff --git a/src/signify/app/contacting.py b/src/signify/app/contacting.py new file mode 100644 index 0000000..2d3f735 --- /dev/null +++ b/src/signify/app/contacting.py @@ -0,0 +1,34 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.contacting module + +""" +from signify.app.clienting import SignifyClient + + +class Contacts: + """ Domain class for accessing Endpoint Role Authorizations """ + + def __init__(self, client: SignifyClient): + self.client = client + + def list(self, start=0, end=24): + """ Returns list of notifications + + Parameters: + start (int): start index of list of notifications, defaults to 0 + end (int): end index of list of notifications, defaults to 24 + + Returns: + dict: data with start, end, total and notes of list result + + """ + headers = dict(Range=f"contacts={start}-{end}") + res = self.client.get(f"/contacts", headers=headers) + # cr = res.headers["content-range"] + # start, end, total = httping.parseRangeHeader(cr, "notes") + + contacts = res.json() + return dict(start=0, end=len(contacts), total=len(contacts), contacts=contacts) + diff --git a/src/signify/app/coring.py b/src/signify/app/coring.py index 13ab355..05d8a65 100644 --- a/src/signify/app/coring.py +++ b/src/signify/app/coring.py @@ -4,13 +4,6 @@ signify.app.coring module """ -from math import ceil - -from keri import kering -from keri.app.keeping import SaltyCreator -from keri.core import eventing -from keri.core.coring import Tiers, MtrDex, Diger, Tholder -from keri.kering import Roles from signify.app.clienting import SignifyClient diff --git a/src/signify/app/credentialing.py b/src/signify/app/credentialing.py index 3a84d26..480b8c7 100644 --- a/src/signify/app/credentialing.py +++ b/src/signify/app/credentialing.py @@ -7,7 +7,9 @@ from collections import namedtuple from keri.core import coring -from keri.core.eventing import TraitDex +from keri.core.eventing import TraitDex, interact +from keri.help import helping +from keri.vc import proving from keri.vdr import eventing from signify.app.clienting import SignifyClient @@ -19,22 +21,88 @@ class Registries: - def registryIncept(self, hab, body): + def __init__(self, client: SignifyClient): + """ Create domain class for working with credentials for a single AID + + Parameters: + client (SignifyClient): Signify client class for access resources on a KERIA service instance + + """ + self.client = client + + def get(self, name, registryName): + res = self.client.get(f"/identifiers/{name}/registries/{registryName}") + return res.json() + + def create(self, hab, registryName, noBackers=True, estOnly=False, baks=None, toad=0, nonce=None): + baks = baks if baks is not None else [] + + pre = hab["prefix"] + cnfg = [] - if "noBackers" in body and body["noBackers"]: + if noBackers: cnfg.append(TraitDex.NoBackers) - baks = body["baks"] if "baks" in body else None - toad = body["toad"] if "toad" in body else None - estOnly = body["estOnly"] if "estOnly" in body else False - nonce = body["nonce"] if "nonce" in body else None + if estOnly: + cnfg.append(TraitDex.EstOnly) - regser = eventing.incept(hab.pre, + regser = eventing.incept(pre, baks=baks, toad=toad, nonce=nonce, cnfg=cnfg, code=coring.MtrDex.Blake3_256) + state = hab["state"] + sn = int(state["s"], 16) + dig = state["d"] + + rseal = dict(i=regser.pre, s="0", d=regser.pre) + data = [rseal] + + serder = interact(pre, sn=sn + 1, data=data, dig=dig) + + keeper = self.client.manager.get(aid=hab) + sigs = keeper.sign(ser=serder.raw) + + op = self.create_from_events(hab=hab, registryName=registryName, vcp=regser.ked, ixn=serder.ked, sigs=sigs) + + return regser, serder, sigs, op + + def create_from_events(self, hab, registryName, vcp, ixn, sigs): + body = dict( + name=registryName, + vcp=vcp, + ixn=ixn, + sigs=sigs + ) + keeper = self.client.manager.get(aid=hab) + body[keeper.algo] = keeper.params() + name = hab["name"] + + resp = self.client.post(path=f"/identifiers/{name}/registries", json=body) + return resp.json() + + @staticmethod + def serialize(serder, anc): + seqner = coring.Seqner(sn=anc.sn) + couple = seqner.qb64b + anc.said.encode("utf-8") + atc = bytearray() + atc.extend(coring.Counter(code=coring.CtrDex.SealSourceCouples, + count=1).qb64b) + atc.extend(couple) + + # prepend pipelining counter to attachments + if len(atc) % 4: + raise ValueError("Invalid attachments size={}, nonintegral" + " quadlets.".format(len(atc))) + pcnt = coring.Counter(code=coring.CtrDex.AttachedMaterialQuadlets, + count=(len(atc) // 4)).qb64b + msg = bytearray(serder.raw) + msg.extend(pcnt) + msg.extend(atc) + + return msg + class Credentials: """ Domain class for accessing, presenting, issuing and revoking credentials """ @@ -48,11 +116,10 @@ def __init__(self, client: SignifyClient): """ self.client = client - def list(self, name, filtr=None, sort=None, skip=None, limit=None): + def list(self, filtr=None, sort=None, skip=None, limit=None): """ Parameters: - name (str): Alias associated with the AID filtr (dict): Credential filter dict sort(list): list of SAD Path field references to sort by skip (int): number of credentials to skip at the front of the list @@ -74,7 +141,7 @@ def list(self, name, filtr=None, sort=None, skip=None, limit=None): limt=limit ) - res = self.client.post(f"/identifiers/{name}/credentials/query", json=json) + res = self.client.post(f"/credentials/query", json=json) return res.json() def export(self, name, said): @@ -91,3 +158,180 @@ def export(self, name, said): res = self.client.get(f"/identifiers/{name}/credentials/{said}", headers=headers) return res.content + + def create(self, hab, registry, data, schema, recipient=None, edges=None, rules=None, private=False, + timestamp=None): + """ Create and submit a credential + + Parameters: + hab: + registry: + data: + schema: + recipient: + edges: + rules: + private: + timestamp: + + Returns: + + """ + pre = hab["prefix"] + + if recipient is None: + recp = None + else: + recp = recipient + + if timestamp is not None: + data["dt"] = timestamp + + regk = registry['regk'] + creder = proving.credential(issuer=registry['pre'], + schema=schema, + recipient=recp, + data=data, + source=edges, + private=private, + rules=rules, + status=regk) + + dt = creder.attrib["dt"] if "dt" in creder.attrib else helping.nowIso8601() + noBackers = 'NB' in registry['state']['c'] + if noBackers: + iserder = eventing.issue(vcdig=creder.said, regk=regk, dt=dt) + else: + regi = registry['state']['s'] + regd = registry['state']['d'] + iserder = eventing.backerIssue(vcdig=creder.said, regk=regk, regsn=regi, regd=regd, dt=dt) + + vcid = iserder.ked["i"] + rseq = coring.Seqner(snh=iserder.ked["s"]) + rseal = eventing.SealEvent(vcid, rseq.snh, iserder.said) + rseal = dict(i=rseal.i, s=rseal.s, d=rseal.d) + + data = [rseal] + + state = hab["state"] + sn = int(state["s"], 16) + dig = state["d"] + anc = interact(pre, sn=sn + 1, data=data, dig=dig) + + keeper = self.client.manager.get(aid=hab) + sigs = keeper.sign(ser=anc.raw) + + res = self.create_from_events(hab=hab, creder=creder.sad, iss=iserder.sad, anc=anc.sad, + sigs=sigs) + + return creder, iserder, anc, sigs, res.json() + + def create_from_events(self, hab, creder, iss, anc, sigs): + body = dict( + acdc=creder, + iss=iss, + ixn=anc, + sigs=sigs + ) + keeper = self.client.manager.get(aid=hab) + body[keeper.algo] = keeper.params() + name = hab["name"] + + return self.client.post(f"/identifiers/{name}/credentials", json=body) + + +class Ipex: + def __init__(self, client: SignifyClient): + """ Create domain class for working with credentials for a single AID + + Parameters: + client (SignifyClient): Signify client class for access resources on a KERIA service instance + + """ + self.client = client + + def grant(self, hab, recp, message, acdc, iss, anc, agree=None, dt=None): + exchanges = self.client.exchanges() + data = dict( + m=message, + i=recp, + ) + + embeds = dict( + acdc=acdc, + iss=iss, + anc=anc + ) + + kwa = dict() + if agree is not None: + kwa['dig'] = agree.said + + grant, gsigs, end = exchanges.createExchangeMessage(sender=hab, route="/ipex/grant", + payload=data, embeds=embeds, dt=dt) + + return grant, gsigs, end + + def submitGrant(self, name, exn, sigs, atc, recp): + """ Send precreated grant message to recipients + + Parameters: + name (str): human readable identifier alias to send from + exn (Serder): peer-to-peer message to send + sigs (list): qb64 signatures over the exn + atc (string|bytes): additional attachments for exn (usually pathed signatures over embeds) + recp (list[string]): qb64 recipient AID + + Returns: + dict: operation response from KERIA + + """ + + body = dict( + exn=exn.ked, + sigs=sigs, + atc=atc, + rec=recp + ) + + res = self.client.post(f"/identifiers/{name}/ipex/grant", json=body) + return res.json() + + def admit(self, hab, message, grant, dt=None): + if not grant: + raise ValueError(f"invalid grant={grant}") + + exchanges = self.client.exchanges() + data = dict( + m=message, + ) + + admit, asigs, end = exchanges.createExchangeMessage(sender=hab, route="/ipex/admit", + payload=data, embeds=None, dt=dt, dig=grant) + + return admit, asigs, end + + def submitAdmit(self, name, exn, sigs, atc, recp): + """ Send precreated exn message to recipients + + Parameters: + name (str): human readable identifier alias to send from + exn (bytes): stream byte string of peer-to-peer message to send + admit (bytes): stream byte string of admit exn message + recp (list[string]): qb64 recipient AID + + Returns: + dict: operation response from KERIA + + """ + + body = dict( + exn=exn.ked, + sigs=sigs, + atc=atc, + rec=recp + ) + + res = self.client.post(f"/identifiers/{name}/ipex/admit", json=body) + return res.json() + diff --git a/src/signify/app/escrowing.py b/src/signify/app/escrowing.py index e2b8d88..ff62772 100644 --- a/src/signify/app/escrowing.py +++ b/src/signify/app/escrowing.py @@ -20,7 +20,3 @@ def getEscrowReply(self, route=None): res = self.client.get(f"/escrows/rpy", params=params) return res.json() - - - - diff --git a/src/signify/app/grouping.py b/src/signify/app/grouping.py new file mode 100644 index 0000000..49720e8 --- /dev/null +++ b/src/signify/app/grouping.py @@ -0,0 +1,87 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.grouping module + +""" + +from signify.app.clienting import SignifyClient + + +class Groups: + """ Domain class for performing operations on and with group multisig AIDs """ + + def __init__(self, client: SignifyClient): + """ Create domain class for working with credentials for a single AID + + Parameters: + client (SignifyClient): Signify client class for access resources on a KERIA service instance + + """ + self.client = client + + def get_request(self, said): + """ Retrieve full request status of multisig conversation using the SAID of an EXN + + All matching EXNs and attachments are returning with embed sections matching this EXN. + + Parameters: + said(str): qb64 SAID of the embed section of a multisig exn message + + Returns: + list: list of dicts of exns matching this conversation + + """ + + res = self.client.get(f"/multisig/request/{said}") + return res.json() + + def send_request(self, name, exn, sigs, atc): + """ Send multisig exn peer-to-peer message to other members of the multisig group + + Parameters: + name: + exn: + sigs: + atc: + + Returns: + dict: ked of sent exn message + """ + + body = dict( + exn=exn, + sigs=sigs, + atc=atc.decode("utf-8") + ) + + res = self.client.post(f"/identifiers/{name}/multisig/request", json=body) + return res.json() + + def join(self, name, rot, sigs, gid, smids, rmids): + """ + + Parameters: + name: + rot: + sigs: + gid: + smids: + rmids: + + Returns: + dict: Operation + + """ + + body = dict( + tpc='multisig', + rot=rot.ked, + sigs=sigs, + gid=gid, + smids=smids, + rmids=rmids, + ) + + res = self.client.post(f"/identifiers/{name}/multisig/join", json=body) + return res.json() diff --git a/src/signify/app/notifying.py b/src/signify/app/notifying.py new file mode 100644 index 0000000..8d4d052 --- /dev/null +++ b/src/signify/app/notifying.py @@ -0,0 +1,59 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.notifying module + +""" +from signify.app.clienting import SignifyClient +from signify.core import httping + + +class Notifications: + """ Domain class for accessing Endpoint Role Authorizations """ + + def __init__(self, client: SignifyClient): + self.client = client + + def list(self, start=0, end=24): + """ Returns list of notifications + + Parameters: + start (int): start index of list of notifications, defaults to 0 + end (int): end index of list of notifications, defaults to 24 + + Returns: + dict: data with start, end, total and notes of list result + + """ + headers = dict(Range=f"notes={start}-{end}") + res = self.client.get(f"/notifications", headers=headers) + cr = res.headers["content-range"] + start, end, total = httping.parseRangeHeader(cr, "notes") + + return dict(start=start, end=end, total=total, notes=res.json()) + + def markAsRead(self, nid): + """ Mark notification as read + + Parameters: + nid (str): qb64 SAID of notification to mark as read + + Returns: + bool: True means notification marked as read + + """ + res = self.client.put(f"/notifications/{nid}", json={}) + return res.status_code == 202 + + def delete(self, nid): + """ Delete notification + + Parameters: + nid(str): qb64 SAID of notification to delete + + Returns: + bool: True means notification deleted + + """ + res = self.client.delete(path=f"/notifications/{nid}") + return res.status_code == 202 diff --git a/src/signify/core/authing.py b/src/signify/core/authing.py index 1112c7d..1bcab69 100644 --- a/src/signify/core/authing.py +++ b/src/signify/core/authing.py @@ -7,10 +7,11 @@ from urllib.parse import urlparse from keri import kering -from keri.app.keeping import SaltyCreator -from keri.core import coring, eventing +from keri.app import keeping +from keri.core import coring, eventing, serdering from keri.end import ending +from signify.signifying import State class Agent: @@ -45,7 +46,7 @@ def __init__(self, bran, tier, state=None): self.tier = tier self.salter = coring.Salter(qb64=self.bran) - creator = SaltyCreator(salt=self.salter.qb64, stem=self.stem, tier=tier) + creator = keeping.SaltyCreator(salt=self.salter.qb64, stem=self.stem, tier=tier) self.signer = creator.create(ridx=0, tier=tier).pop() self.nsigner = creator.create(ridx=0 + 1, tier=tier).pop() @@ -63,12 +64,8 @@ def event(self): siger = self.signer.sign(ser=self.serder.raw, index=0) return self.serder, siger - @property - def verfers(self): - return self.signer.verfers - def derive(self, state): - if state is None or state['ee']['s'] == "0": + if state is None or (type(state) is dict and state['ee']['s'] == "0"): return eventing.incept(keys=self.keys, isith="1", nsith="1", @@ -76,8 +73,8 @@ def derive(self, state): code=coring.MtrDex.Blake3_256, toad="0", wits=[]) - else: - return coring.Serder(ked=state.controller['ee']) + elif type(state) is State: + return serdering.SerderKERI(sad=state.controller['ee']) def approveDelegation(self, agent): seqner = coring.Seqner(sn=agent.sn) @@ -125,10 +122,11 @@ def rotate(self, nbran, aids): nsigner = self.salter.signer(transferable=False) # This is the previous next signer so it will be used to sign the rotation and then have 0 signing authority - creator = SaltyCreator(salt=self.salter.qb64, stem=self.stem, tier=self.tier) + #here + creator = keeping.SaltyCreator(salt=self.salter.qb64, stem=self.stem, tier=self.tier) signer = creator.create(ridx=0 + 1, tier=self.tier).pop() - ncreator = SaltyCreator(salt=nsalter.qb64, stem=self.stem, tier=self.tier) + ncreator = keeping.SaltyCreator(salt=nsalter.qb64, stem=self.stem, tier=self.tier) self.signer = ncreator.create(ridx=0, tier=self.tier).pop() self.nsigner = ncreator.create(ridx=0 + 1, tier=self.tier).pop() @@ -167,7 +165,7 @@ def rotate(self, nbran, aids): dnxt = decrypter.decrypt(cipher=cipher).qb64 # Now we have the AID salt, use it to verify against the current public keys - acreator = SaltyCreator(dnxt, stem=salty["stem"], tier=salty["tier"]) + acreator = keeping.SaltyCreator(dnxt, stem=salty["stem"], tier=salty["tier"]) signers = acreator.create(codes=salty["icodes"], pidx=salty["pidx"], kidx=salty["kidx"], transferable=salty["transferable"]) pubs = aid["state"]["k"] @@ -235,6 +233,9 @@ def __init__(self, agent: Agent, ctrl: Controller): def verify(self, rep, **kwargs): url = urlparse(rep.request.url) + if "SIGNIFY-RESOURCE" not in rep.headers: + raise kering.AuthNError("No valid signature from agent on response.") + resource = rep.headers["SIGNIFY-RESOURCE"] if resource != self.agent.pre or not self.verifysig(rep.headers, rep.request.method, url.path): raise kering.AuthNError("No valid signature from agent on response.") @@ -309,7 +310,7 @@ def sign(self, headers, method, path, fields=None): fields (Optional[list]): Optional list of Signature Input fields to sign. Returns: - headers (Hict): Modified headers with new Signature and Signature Input fields + headers (dict): Modified headers with new Signature and Signature Input fields """ diff --git a/src/signify/core/httping.py b/src/signify/core/httping.py index 4c70fdd..0b2e93b 100644 --- a/src/signify/core/httping.py +++ b/src/signify/core/httping.py @@ -4,18 +4,20 @@ signify.core.httping module """ +from typing import Tuple -def parseRangeHeader(header: str): +def parseRangeHeader(header: str, typ: str) -> Tuple[int, int, int]: """ Parse start, end and total from HTTP Content-Range header value Parameters: header (str): HTTP Content-Range header value + typ (str): Type in range header - Returns: + Returns: Tuple[int, int int] """ - data = header.lstrip("aids ") + data = header.lstrip(f"{typ} ") values = data.split("/") rng = values[0].split("-") diff --git a/src/signify/core/keeping.py b/src/signify/core/keeping.py index bb9d633..ae5f117 100644 --- a/src/signify/core/keeping.py +++ b/src/signify/core/keeping.py @@ -9,7 +9,7 @@ import importlib from keri import kering -from keri.app.keeping import SaltyCreator, Algos, RandyCreator +from keri.app import keeping from keri.core import coring from keri.core.coring import Tiers, MtrDex @@ -32,16 +32,16 @@ def __init__(self, salter, extern_modules=None): def new(self, algo, pidx, **kwargs): match algo: - case Algos.salty: + case keeping.Algos.salty: return SaltyKeeper(salter=self.salter, pidx=pidx, **kwargs) - case Algos.group: + case keeping.Algos.group: return GroupKeeper(mgr=self, **kwargs) - case Algos.randy: + case keeping.Algos.randy: return RandyKeeper(salter=self.salter, **kwargs) - case Algos.extern: + case keeping.Algos.extern: typ = kwargs["extern_type"] if typ not in self.modules: raise kering.ConfigurationError(f"unsupported external module type {typ}") @@ -52,16 +52,18 @@ def new(self, algo, pidx, **kwargs): def get(self, aid): pre = coring.Prefixer(qb64=aid["prefix"]) - if Algos.salty in aid: - kwargs = aid[Algos.salty] + if keeping.Algos.salty in aid: + kwargs = aid[keeping.Algos.salty] + if "pidx" not in kwargs: + raise kering.ConfigurationError(f"missing pidx in {kwargs}") return SaltyKeeper(salter=self.salter, **kwargs) - elif Algos.randy in aid: - kwargs = aid[Algos.randy] + elif keeping.Algos.randy in aid: + kwargs = aid[keeping.Algos.randy] return RandyKeeper(salter=self.salter, transferable=pre.transferable, **kwargs) - elif Algos.group in aid: - kwargs = aid[Algos.group] + elif keeping.Algos.group in aid: + kwargs = aid[keeping.Algos.group] return GroupKeeper(mgr=self, **kwargs) @@ -70,13 +72,13 @@ class BaseKeeper: @property def algo(self): if isinstance(self, SaltyKeeper): - return Algos.salty + return keeping.Algos.salty elif isinstance(self, RandyKeeper): - return Algos.randy + return keeping.Algos.randy elif isinstance(self, GroupKeeper): - return Algos.group + return keeping.Algos.group else: - return Algos.extern + return keeping.Algos.extern @staticmethod def __sign__(ser, signers, indexed=False, indices=None, ondices=None): @@ -174,17 +176,15 @@ def __init__(self, salter, pidx, kidx=0, tier=Tiers.low, transferable=False, ste # sxlt is encrypted salt for this AID or None if incepting if bran is not None: bran = coring.MtrDex.Salt_128 + 'A' + bran[:21] - self.creator = SaltyCreator(salt=bran, stem=stem, tier=tier) + self.creator = keeping.SaltyCreator(salt=bran, stem=stem, tier=tier) self.sxlt = self.encrypter.encrypt(self.creator.salt).qb64 - elif sxlt is None: - self.creator = SaltyCreator(stem=stem, tier=tier) + self.creator = keeping.SaltyCreator(stem=stem, tier=tier) self.sxlt = self.encrypter.encrypt(self.creator.salt).qb64 - else: self.sxlt = sxlt ciph = coring.Cipher(qb64=self.sxlt) - self.creator = SaltyCreator(self.decrypter.decrypt(cipher=ciph).qb64, stem=stem, tier=tier) + self.creator = keeping.SaltyCreator(self.decrypter.decrypt(cipher=ciph).qb64, stem=stem, tier=tier) def params(self): """ Get AID parameters to store externally """ @@ -290,7 +290,7 @@ def __init__(self, salter, code=MtrDex.Ed25519_Seed, count=1, icodes=None, trans self.ncodes = ncodes self.dcode = dcode - self.creator = RandyCreator() + self.creator = keeping.RandyCreator() def params(self): return dict( @@ -355,7 +355,7 @@ def rotate(self, states, rstates, **_): return self.gkeys, self.gdigs - def sign(self, ser, indexed=True, rotate=False, **_): + def sign(self, ser, indexed=True, **_): key = self.mhab['state']['k'][0] ndig = self.mhab['state']['n'][0] diff --git a/src/signify/peer/__init__.py b/src/signify/peer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/signify/peer/exchanging.py b/src/signify/peer/exchanging.py new file mode 100644 index 0000000..d78b0fd --- /dev/null +++ b/src/signify/peer/exchanging.py @@ -0,0 +1,116 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.exchanging module + +""" +from keri.peer import exchanging + +from signify.app.clienting import SignifyClient + + +class Exchanges: + """ Domain class for performing operations on and with group multisig AIDs """ + + def __init__(self, client: SignifyClient): + """ Create domain class for working with credentials for a single AID + + Parameters: + client (SignifyClient): Signify client class for access resources on a KERIA service instance + + """ + self.client = client + + def send(self, name, topic, sender, route, payload, embeds, recipients, dig=None): + """ Send exn message to recipients + + Parameters: + name (str): human readable identifier alias to send from + topic (Str): message topic + sender (dict): Identifier dict from identifiers.get + route (str): exn route field + payload (dict): payload of the exn message + embeds (dict): map of label to bytes of encoded KERI event to embed in exn + recipients (list[string]): list of qb64 recipient AIDs + dig (str): Optional qb64 SAID of exchange message reverse chain + + Returns: + dict: operation response from KERIA + + """ + + exn, sigs, atc = self.createExchangeMessage(sender, route, payload, embeds, dig=dig) + json = self.sendFromEvents(name, topic, exn=exn, sigs=sigs, atc=atc, recipients=recipients) + + return exn, sigs, json + + def createExchangeMessage(self, sender, route, payload, embeds, dig=None, dt=None): + """ Create exn message from parameters and return Serder with signatures and additional attachments. + + Parameters: + sender (dict): Identifier dict from identifiers.get + route (str): exn route field + payload (dict): payload of the exn message + embeds (dict): map of label to bytes of encoded KERI event to embed in exn + dig (str): Optional qb64 SAID of exchange message reverse chain + dt (str): Iso formatted date string + + Returns: + (exn, sigs, end): tuple of Serder, list, bytes of event, signatures over the event and any transposed + attachments from embeds + + """ + + keeper = self.client.manager.get(sender) + + exn, end = exchanging.exchange(route=route, + payload=payload, + sender=sender["prefix"], + embeds=embeds, + dig=dig, + date=dt) + + sigs = keeper.sign(ser=exn.raw) + + return exn, sigs, bytes(end).decode("utf-8") + + def sendFromEvents(self, name, topic, exn, sigs, atc, recipients): + """ Send precreated exn message to recipients + + Parameters: + name (str): human readable identifier alias to send from + topic (Str): message topic + exn (SerderKERI): peer-to-peer message to send + sigs (list): qb64 signatures over the exn + atc (string|bytes): additional attachments for exn (usually pathed signatures over embeds) + recipients (list[string]): list of qb64 recipient AIDs + + Returns: + dict: operation response from KERIA + + """ + + body = dict( + tpc=topic, + exn=exn.ked, + sigs=sigs, + atc=atc, + rec=recipients + ) + + res = self.client.post(f"/identifiers/{name}/exchanges", json=body) + return res.json() + + def get(self, said): + """ + + Parameters: + said (str): qb64 SAID of the exn message to retrieve + + Returns: + dict: exn message + + """ + + res = self.client.get(f"/exchanges/{said}") + return res.json() diff --git a/src/signify/signifying.py b/src/signify/signifying.py new file mode 100644 index 0000000..b3eb7aa --- /dev/null +++ b/src/signify/signifying.py @@ -0,0 +1,16 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.signifying + +""" + +from dataclasses import dataclass + + +@dataclass +class State: + controller: dict = None + agent : dict = None + ridx: int = None + pidx: int = None \ No newline at end of file diff --git a/tests/app/connect.toml b/tests/app/connect.toml deleted file mode 100644 index 551b105..0000000 --- a/tests/app/connect.toml +++ /dev/null @@ -1,215 +0,0 @@ -responses: -- response: - auto_calculate_content_length: false - body: '{"title": "404 Not Found", "description": "not agent found for controller - EOgQvKz8ziRn7FdR_ebwK9BkaVOnGeXQOJ87N6hMLrK0"}' - content_type: text/plain - method: GET - status: 404 - url: http://localhost:3901/agent/EOgQvKz8ziRn7FdR_ebwK9BkaVOnGeXQOJ87N6hMLrK0 -- response: - auto_calculate_content_length: false - body: '' - content_type: text/plain - method: POST - status: 202 - url: http://localhost:3903/boot -- response: - auto_calculate_content_length: false - body: '{"agent": {"v": "KERI10JSON0001ed_", "vn": [1, 0], "i": "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei", - "s": "0", "p": "", "d": "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei", "f": - "0", "dt": "2023-06-13T19:58:09.044607+00:00", "et": "dip", "kt": "1", "k": - ["DMZh_y-H5C3cSbZZST-fqnsmdNTReZxIh0t2xSTOJQ8a"], "nt": "1", "n": ["EM9M2EQNCBK0MyAhVYBvR98Q0tefpvHgE-lHLs82XgqC"], - "bt": "0", "b": [], "c": [], "ee": {"s": "0", "d": "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei", - "br": [], "ba": []}, "di": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose"}, - "controller": {"state": {"v": "KERI10JSON0001c1_", "vn": [1, 0], "i": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", - "s": "0", "p": "", "d": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", "f": - "0", "dt": "2023-06-13T19:58:09.054967+00:00", "et": "icp", "kt": "1", "k": - ["DAbWjobbaLqRB94KiAutAHb_qzPpOHm3LURA_ksxetVc"], "nt": "1", "n": ["EIFG_uqfr1yN560LoHYHfvPAhxQ5sN6xZZT_E3h7d2tL"], - "bt": "0", "b": [], "c": [], "ee": {"s": "0", "d": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", - "br": [], "ba": []}, "di": ""}, "ee": {"v": "KERI10JSON00012b_", "t": "icp", - "d": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", "i": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", - "s": "0", "kt": "1", "k": ["DAbWjobbaLqRB94KiAutAHb_qzPpOHm3LURA_ksxetVc"], - "nt": "1", "n": ["EIFG_uqfr1yN560LoHYHfvPAhxQ5sN6xZZT_E3h7d2tL"], "bt": "0", - "b": [], "c": [], "a": []}}, "pidx": 0}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/agent/ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose -- response: - auto_calculate_content_length: false - body: '' - content_type: text/plain - method: PUT - status: 204 - url: http://localhost:3901/agent/ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose?type=ixn -- response: - auto_calculate_content_length: false - body: '[]' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/identifiers?last=&limit=25 -- response: - auto_calculate_content_length: false - body: '{"name": "done.ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "metadata": - {"response": {"v": "KERI10JSON00012b_", "t": "icp", "d": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "s": "0", "kt": "1", "k": - ["DPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9"], "nt": "1", "n": ["EAORnRtObOgNiOlMolji-KijC_isa3lRDpHCsol79cOc"], - "bt": "0", "b": [], "c": [], "a": []}}, "done": true, "error": null, "response": - {"v": "KERI10JSON00012b_", "t": "icp", "d": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "s": "0", "kt": "1", "k": - ["DPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9"], "nt": "1", "n": ["EAORnRtObOgNiOlMolji-KijC_isa3lRDpHCsol79cOc"], - "bt": "0", "b": [], "c": [], "a": []}}' - content_type: text/plain - method: POST - status: 202 - url: http://localhost:3901/identifiers -- response: - auto_calculate_content_length: false - body: '[{"name": "aid1", "prefix": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "salty": {"sxlt": "1AAHuB05JJ0zxCvjuV6EnmocRziLI85BsE_W_eOGUaKMwEMWeOdRYW6O6EYfGFi2UxF9uFi-i4Y-RlB1VxDIbhmXczxVEuOX7cgd", - "pidx": 0, "kidx": 0, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A"], "ncodes": ["A"], "transferable": true}}]' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/identifiers?last=&limit=25 -- response: - auto_calculate_content_length: false - body: '{"name": "done.EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX", "metadata": - {"response": {"v": "KERI10JSON0001e7_", "t": "icp", "d": "EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX", - "i": "EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX", "s": "0", "kt": "2", "k": - ["DGBw7C7AfC7jbD3jLLRS3SzIWFndM947TyNWKQ52iQx5", "DD_bHYFsgWXuCbz3SD0HjCIe_ITjRvEoCGuZ4PcNFFDz", - "DEe9u8k0fm1wMFAuOIsCtCNrpduoaV5R21rAcJl0awze"], "nt": "2", "n": ["EML5FrjCpz8SEl4dh0U15l8bMRhV_O5iDcR1opLJGBSH", - "EJpKquuibYTqpwMDqEFAFs0gwq0PASAHZ_iDmSF3I2Vg", "ELplTAiEKdobFhlf-dh1vUb2iVDW0dYOSzs1dR7fQo60"], - "bt": "0", "b": [], "c": [], "a": []}}, "done": true, "error": null, "response": - {"v": "KERI10JSON0001e7_", "t": "icp", "d": "EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX", - "i": "EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX", "s": "0", "kt": "2", "k": - ["DGBw7C7AfC7jbD3jLLRS3SzIWFndM947TyNWKQ52iQx5", "DD_bHYFsgWXuCbz3SD0HjCIe_ITjRvEoCGuZ4PcNFFDz", - "DEe9u8k0fm1wMFAuOIsCtCNrpduoaV5R21rAcJl0awze"], "nt": "2", "n": ["EML5FrjCpz8SEl4dh0U15l8bMRhV_O5iDcR1opLJGBSH", - "EJpKquuibYTqpwMDqEFAFs0gwq0PASAHZ_iDmSF3I2Vg", "ELplTAiEKdobFhlf-dh1vUb2iVDW0dYOSzs1dR7fQo60"], - "bt": "0", "b": [], "c": [], "a": []}}' - content_type: text/plain - method: POST - status: 202 - url: http://localhost:3901/identifiers -- response: - auto_calculate_content_length: false - body: '[{"name": "aid1", "prefix": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "salty": {"sxlt": "1AAHuB05JJ0zxCvjuV6EnmocRziLI85BsE_W_eOGUaKMwEMWeOdRYW6O6EYfGFi2UxF9uFi-i4Y-RlB1VxDIbhmXczxVEuOX7cgd", - "pidx": 0, "kidx": 0, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A"], "ncodes": ["A"], "transferable": true}}, {"name": "aid2", "prefix": "EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX", - "salty": {"sxlt": "1AAHKyTELxYKVCjg3E0yZ_XlaLMkG_fPFijSJ0Mdse6fvDttB72XslQMOI6yqI0kY9Y3lArzO2d6JOhLzM3CCloA2PCZnrFMawNK", - "pidx": 1, "kidx": 0, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A", "A", "A"], "ncodes": ["A", "A", "A"], "transferable": true}}]' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/identifiers?last=&limit=25 -- response: - auto_calculate_content_length: false - body: '{"name": "aid1", "prefix": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "salty": {"sxlt": "1AAHuB05JJ0zxCvjuV6EnmocRziLI85BsE_W_eOGUaKMwEMWeOdRYW6O6EYfGFi2UxF9uFi-i4Y-RlB1VxDIbhmXczxVEuOX7cgd", - "pidx": 0, "kidx": 0, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A"], "ncodes": ["A"], "transferable": true}, "transferable": true, "state": - {"v": "KERI10JSON0001c1_", "vn": [1, 0], "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "s": "0", "p": "", "d": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "f": - "0", "dt": "2023-06-13T19:58:09.519568+00:00", "et": "icp", "kt": "1", "k": - ["DPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9"], "nt": "1", "n": ["EAORnRtObOgNiOlMolji-KijC_isa3lRDpHCsol79cOc"], - "bt": "0", "b": [], "c": [], "ee": {"s": "0", "d": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "br": [], "ba": []}, "di": ""}, "windexes": []}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/identifiers/aid1 -- response: - auto_calculate_content_length: false - body: '{"name": "done.ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "metadata": - {"response": {"v": "KERI10JSON000160_", "t": "rot", "d": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", - "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "s": "1", "p": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "kt": "1", "k": ["DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly"], "nt": "1", - "n": ["EJMovBlrBuD6BVeUsGSxLjczbLEbZU9YnTSud9K4nVzk"], "bt": "0", "br": [], - "ba": [], "a": []}}, "done": true, "error": null, "response": {"v": "KERI10JSON000160_", - "t": "rot", "d": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "s": "1", "p": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "kt": "1", "k": - ["DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly"], "nt": "1", "n": ["EJMovBlrBuD6BVeUsGSxLjczbLEbZU9YnTSud9K4nVzk"], - "bt": "0", "br": [], "ba": [], "a": []}}' - content_type: text/plain - method: PUT - status: 200 - url: http://localhost:3901/identifiers/aid1 -- response: - auto_calculate_content_length: false - body: '{"name": "aid1", "prefix": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "salty": {"sxlt": "1AAHuB05JJ0zxCvjuV6EnmocRziLI85BsE_W_eOGUaKMwEMWeOdRYW6O6EYfGFi2UxF9uFi-i4Y-RlB1VxDIbhmXczxVEuOX7cgd", - "pidx": 0, "kidx": 1, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A"], "ncodes": ["A"], "transferable": true}, "transferable": true, "state": - {"v": "KERI10JSON0001ed_", "vn": [1, 0], "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "s": "1", "p": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "d": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", - "f": "1", "dt": "2023-06-13T19:58:10.550886+00:00", "et": "rot", "kt": "1", - "k": ["DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly"], "nt": "1", "n": ["EJMovBlrBuD6BVeUsGSxLjczbLEbZU9YnTSud9K4nVzk"], - "bt": "0", "b": [], "c": [], "ee": {"s": "1", "d": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", - "br": [], "ba": []}, "di": ""}, "windexes": []}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/identifiers/aid1 -- response: - auto_calculate_content_length: false - body: '{"name": "done.ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "metadata": - {"response": {"v": "KERI10JSON0000f9_", "t": "ixn", "d": "ENsmRAg_oM7Hl1S-GTRMA7s4y760lQMjzl0aqOQ2iTce", - "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "s": "2", "p": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", - "a": ["ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK"]}}, "done": true, "error": - null, "response": {"v": "KERI10JSON0000f9_", "t": "ixn", "d": "ENsmRAg_oM7Hl1S-GTRMA7s4y760lQMjzl0aqOQ2iTce", - "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "s": "2", "p": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", - "a": ["ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK"]}}' - content_type: text/plain - method: PUT - status: 200 - url: http://localhost:3901/identifiers/aid1?type=ixn -- response: - auto_calculate_content_length: false - body: '{"name": "aid1", "prefix": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "salty": {"sxlt": "1AAHuB05JJ0zxCvjuV6EnmocRziLI85BsE_W_eOGUaKMwEMWeOdRYW6O6EYfGFi2UxF9uFi-i4Y-RlB1VxDIbhmXczxVEuOX7cgd", - "pidx": 0, "kidx": 1, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A"], "ncodes": ["A"], "transferable": true}, "transferable": true, "state": - {"v": "KERI10JSON0001ed_", "vn": [1, 0], "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "s": "2", "p": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", "d": "ENsmRAg_oM7Hl1S-GTRMA7s4y760lQMjzl0aqOQ2iTce", - "f": "2", "dt": "2023-06-13T19:58:10.738256+00:00", "et": "ixn", "kt": "1", - "k": ["DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly"], "nt": "1", "n": ["EJMovBlrBuD6BVeUsGSxLjczbLEbZU9YnTSud9K4nVzk"], - "bt": "0", "b": [], "c": [], "ee": {"s": "1", "d": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", - "br": [], "ba": []}, "di": ""}, "windexes": []}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/identifiers/aid1 -- response: - auto_calculate_content_length: false - body: '[{"v": "KERI10JSON00012b_", "t": "icp", "d": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "s": "0", "kt": "1", "k": - ["DPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9"], "nt": "1", "n": ["EAORnRtObOgNiOlMolji-KijC_isa3lRDpHCsol79cOc"], - "bt": "0", "b": [], "c": [], "a": []}, {"v": "KERI10JSON000160_", "t": "rot", - "d": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "s": "1", "p": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", "kt": "1", "k": - ["DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly"], "nt": "1", "n": ["EJMovBlrBuD6BVeUsGSxLjczbLEbZU9YnTSud9K4nVzk"], - "bt": "0", "br": [], "ba": [], "a": []}, {"v": "KERI10JSON0000f9_", "t": "ixn", - "d": "ENsmRAg_oM7Hl1S-GTRMA7s4y760lQMjzl0aqOQ2iTce", "i": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "s": "2", "p": "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg", "a": ["ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK"]}]' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/events?pre=ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK -- response: - auto_calculate_content_length: false - body: '[{"name": "aid1", "prefix": "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK", - "salty": {"sxlt": "1AAHuB05JJ0zxCvjuV6EnmocRziLI85BsE_W_eOGUaKMwEMWeOdRYW6O6EYfGFi2UxF9uFi-i4Y-RlB1VxDIbhmXczxVEuOX7cgd", - "pidx": 0, "kidx": 1, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A"], "ncodes": ["A"], "transferable": true}}, {"name": "aid2", "prefix": "EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX", - "salty": {"sxlt": "1AAHKyTELxYKVCjg3E0yZ_XlaLMkG_fPFijSJ0Mdse6fvDttB72XslQMOI6yqI0kY9Y3lArzO2d6JOhLzM3CCloA2PCZnrFMawNK", - "pidx": 1, "kidx": 0, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A", "A", "A"], "ncodes": ["A", "A", "A"], "transferable": true}}]' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/identifiers?last=&limit=25 diff --git a/tests/app/delegation.toml b/tests/app/delegation.toml deleted file mode 100644 index fd9fbb1..0000000 --- a/tests/app/delegation.toml +++ /dev/null @@ -1,128 +0,0 @@ -responses: -- response: - auto_calculate_content_length: false - body: '' - content_type: text/plain - method: POST - status: 202 - url: http://localhost:3903/boot -- response: - auto_calculate_content_length: false - body: '{"agent": {"v": "KERI10JSON0001e2_", "i": "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei", - "s": "0", "p": "", "d": "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei", "f": - "0", "dt": "2023-05-30T19:37:58.780495+00:00", "et": "dip", "kt": "1", "k": - ["DMZh_y-H5C3cSbZZST-fqnsmdNTReZxIh0t2xSTOJQ8a"], "nt": "1", "n": ["EM9M2EQNCBK0MyAhVYBvR98Q0tefpvHgE-lHLs82XgqC"], - "bt": "0", "b": [], "c": [], "ee": {"s": "0", "d": "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei", - "br": [], "ba": []}, "di": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose"}, - "controller": {"state": {"v": "KERI10JSON0001b6_", "i": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", - "s": "0", "p": "", "d": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", "f": - "0", "dt": "2023-05-30T19:38:04.488054+00:00", "et": "icp", "kt": "1", "k": - ["DAbWjobbaLqRB94KiAutAHb_qzPpOHm3LURA_ksxetVc"], "nt": "1", "n": ["EIFG_uqfr1yN560LoHYHfvPAhxQ5sN6xZZT_E3h7d2tL"], - "bt": "0", "b": [], "c": [], "ee": {"s": "0", "d": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", - "br": [], "ba": []}, "di": ""}, "ee": {"v": "KERI10JSON00012b_", "t": "icp", - "d": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", "i": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", - "s": "0", "kt": "1", "k": ["DAbWjobbaLqRB94KiAutAHb_qzPpOHm3LURA_ksxetVc"], - "nt": "1", "n": ["EIFG_uqfr1yN560LoHYHfvPAhxQ5sN6xZZT_E3h7d2tL"], "bt": "0", - "b": [], "c": [], "a": []}}, "pidx": 0}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/agent/ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose -- response: - auto_calculate_content_length: false - body: '' - content_type: text/plain - method: PUT - status: 204 - url: http://localhost:3901/agent/ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose?type=ixn -- response: - auto_calculate_content_length: false - body: '{"name": "oobi.ANtCz37uCXXxvSmTbvkegjfTkq-9v5Nvl4ksTWKJIl-q", "metadata": - {"oobi": "http://127.0.0.1:5642/oobi/EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7/witness"}, - "done": false, "error": null, "response": null}' - content_type: text/plain - method: POST - status: 202 - url: http://localhost:3901/oobis -- response: - auto_calculate_content_length: false - body: '{"name": "oobi.ANtCz37uCXXxvSmTbvkegjfTkq-9v5Nvl4ksTWKJIl-q", "metadata": - {"oobi": "http://127.0.0.1:5642/oobi/EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7/witness"}, - "done": false, "error": null, "response": null}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/operations/oobi.ANtCz37uCXXxvSmTbvkegjfTkq-9v5Nvl4ksTWKJIl-q -- response: - auto_calculate_content_length: false - body: '{"name": "oobi.ANtCz37uCXXxvSmTbvkegjfTkq-9v5Nvl4ksTWKJIl-q", "metadata": - {"oobi": "http://127.0.0.1:5642/oobi/EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7/witness"}, - "done": true, "error": null, "response": {"v": "KERI10JSON000242_", "i": "EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7", - "s": "0", "p": "", "d": "EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7", "f": - "0", "dt": "2023-05-30T19:40:30.019335+00:00", "et": "icp", "kt": "1", "k": - ["DPg4INjWDxsd4DzFCOAL0Kd7ic7TpvpJpUd_tTEVFOZg"], "nt": "1", "n": ["EG1Xs3lMpJAe1GYsiDsWnWl12FvtKKLFVkH_Sf07bay2"], - "bt": "2", "b": ["BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"], "c": [], "ee": {"s": "0", "d": - "EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7", "br": [], "ba": []}, "di": ""}}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/operations/oobi.ANtCz37uCXXxvSmTbvkegjfTkq-9v5Nvl4ksTWKJIl-q -- response: - auto_calculate_content_length: false - body: '{"name": "delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "metadata": - {"pre": "EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "sn": 0}, "done": false, - "error": null, "response": null}' - content_type: text/plain - method: POST - status: 202 - url: http://localhost:3901/identifiers -- response: - auto_calculate_content_length: false - body: '{"name": "delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "metadata": - {"pre": "EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "sn": 0}, "done": false, - "error": null, "response": null}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/operations/delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6 -- response: - auto_calculate_content_length: false - body: '{"name": "delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "metadata": - {"pre": "EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "sn": 0}, "done": false, - "error": null, "response": null}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/operations/delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6 -- response: - auto_calculate_content_length: false - body: '{"name": "delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "metadata": - {"pre": "EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "sn": 0}, "done": false, - "error": null, "response": null}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/operations/delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6 -- response: - auto_calculate_content_length: false - body: '{"name": "delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "metadata": - {"pre": "EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "sn": 0}, "done": false, - "error": null, "response": null}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/operations/delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6 -- response: - auto_calculate_content_length: false - body: '{"name": "delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "metadata": - {"pre": "EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "sn": 0}, "done": true, - "error": null, "response": {"v": "KERI10JSON0001eb_", "t": "dip", "d": "EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", - "i": "EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6", "s": "0", "kt": "1", "k": - ["DLwjfzv7tcZOaMloHAuVngxS_0Y_mt6mVgTSCK3qJE9Q"], "nt": "1", "n": ["EJ_3Y-vjtjGVZboKYUCM-YV0IVq_b0fyRJvkpX8VrGWX"], - "bt": "2", "b": ["BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"], "c": [], "a": [], "di": "EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7"}}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/operations/delegation.EGt2v1ISFYeOiC4Z1ANn-RQGUhWGdKMvmIflM_PELvi6 diff --git a/tests/app/test_aiding.py b/tests/app/test_aiding.py new file mode 100644 index 0000000..203943e --- /dev/null +++ b/tests/app/test_aiding.py @@ -0,0 +1,505 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.test_aiding module + +Testing aiding with unit tests +""" + +import pytest +from mockito import mock, verify, verifyNoUnwantedInteractions, unstub, expect + + +def test_aiding_list(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock({'headers': {'content-range': 'aids 0-10/2'}}, spec=Response, strict=True) + expect(mock_client, times=1).get('/identifiers', headers=dict(Range=f"aids={0}-{24}")).thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn( + ['aid1', 'aid2'] + ) + + out = ids.list() + assert out['start'] == 0 + assert out['end'] == 10 + assert out['total'] == 2 + assert out['aids'] == ['aid1', 'aid2'] + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_get(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + from signify.app.aiding import Identifiers + id = Identifiers(client=client) + + from requests import Response + mock_response = mock(spec=Response, strict=True) + + expect(client, times=1).get('/identifiers/aid1').thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn({'name': 'aid1'}) + + out = id.get(name='aid1') + + assert out['name'] == 'aid1' + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_create(): + from signify.core import keeping + mock_keeper = mock({'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + mock_manager = mock(spec=keeping.Manager, strict=True) + + from mockito import kwargs + expect(mock_manager, times=1).new('salty', 0, **kwargs).thenReturn(mock_keeper) + + keys = ['a signer verfer qb64'] + ndigs = ['next signer digest'] + + expect(mock_keeper, times=1).incept(transferable=True).thenReturn((keys, ndigs)) + + from keri.core import serdering + mock_serder = mock({'raw': b'raw bytes', 'ked': {'a': 'key event dictionary'}}, spec=serdering.SerderKERI, + strict=True) + + from keri.core import eventing + expect(eventing, times=1).incept(keys=keys, isith='1', nsith='1', ndigs=ndigs, code='E', wits=[], toad='0', + cnfg=[], data=[]).thenReturn(mock_serder) + expect(mock_keeper, times=1).sign(mock_serder.raw).thenReturn(['a signature']) + + from signify.app.clienting import SignifyClient + mock_client = mock({'pidx': 0}, spec=SignifyClient, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + from requests import Response + resp = mock({'json': lambda: {'post': 'success'}}, spec=Response, strict=True) + expect(mock_client, times=1).post('/identifiers', json={'name': 'new_aid', 'icp': {'a': 'key event dictionary'}, + 'sigs': ['a signature'], 'proxy': None, + 'salty': {'keeper': 'params'}, + 'smids': ['a smid'], 'rmids': ['a rmid']}).thenReturn(resp) + + ids.create(name='new_aid', states=[{'i': 'a smid'}], rstates=[{'i': 'a rmid'}]) + + assert mock_client.pidx == 1 + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_create_cnfg(): + from signify.core import keeping + mock_keeper = mock({'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + mock_manager = mock(spec=keeping.Manager, strict=True) + + from mockito import kwargs + expect(mock_manager, times=1).new('salty', 0, **kwargs).thenReturn(mock_keeper) + + keys = ['a signer verfer qb64'] + ndigs = ['next signer digest'] + + expect(mock_keeper, times=1).incept(transferable=True).thenReturn((keys, ndigs)) + + from keri.core import serdering + mock_serder = mock({'raw': b'raw bytes', 'ked': {'a': 'key event dictionary'}}, spec=serdering.SerderKERI, + strict=True) + + from keri.core import eventing + expect(eventing, times=1).incept(keys=keys, isith='1', nsith='1', ndigs=ndigs, code='E', wits=[], toad='0', + cnfg=['EO', 'DND'], data=[]).thenReturn(mock_serder) + expect(mock_keeper, times=1).sign(mock_serder.raw).thenReturn(['a signature']) + + from signify.app.clienting import SignifyClient + mock_client = mock({'pidx': 0}, spec=SignifyClient, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + from requests import Response + resp = mock({'json': lambda: {'post': 'success'}}, spec=Response, strict=True) + expect(mock_client, times=1).post('/identifiers', json={'name': 'new_aid', 'icp': {'a': 'key event dictionary'}, + 'sigs': ['a signature'], 'proxy': None, + 'salty': {'keeper': 'params'}}).thenReturn(resp) + + ids.create(name='new_aid', estOnly=True, DnD=True) + + assert mock_client.pidx == 1 + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_create_delegation(): + from signify.core import keeping + mock_keeper = mock({'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + mock_manager = mock(spec=keeping.Manager, strict=True) + + from mockito import kwargs + expect(mock_manager, times=1).new('salty', 0, **kwargs).thenReturn(mock_keeper) + + keys = ['a signer verfer qb64'] + ndigs = ['next signer digest'] + + expect(mock_keeper, times=1).incept(transferable=True).thenReturn((keys, ndigs)) + + from keri.core import serdering + mock_serder = mock({'raw': b'raw bytes', 'ked': {'a': 'key event dictionary'}}, spec=serdering.SerderKERI, + strict=True) + + from keri.core import eventing + expect(eventing, times=1).delcept(keys=['a signer verfer qb64'], + delpre='my delegation', isith='1', nsith='1', + ndigs=['next signer digest'], code='E', wits=[], + toad='0', cnfg=[], data=[]).thenReturn(mock_serder) + expect(mock_keeper, times=1).sign(mock_serder.raw).thenReturn(['a signature']) + + from signify.app.clienting import SignifyClient + mock_client = mock({'pidx': 0}, spec=SignifyClient, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + from requests import Response + resp = mock({'json': lambda: {'post': 'success'}}, spec=Response, strict=True) + expect(mock_client, times=1).post('/identifiers', + json={'name': 'new_aid', 'icp': {'a': 'key event dictionary'}, + 'sigs': ['a signature'], + 'proxy': None, 'salty': {'keeper': 'params'}, 'smids': ['a smid'], + 'rmids': ['a rmid']}).thenReturn(resp) + + ids.create(name='new_aid', delpre='my delegation', states=[{'i': 'a smid'}], rstates=[{'i': 'a rmid'}]) + + assert mock_client.pidx == 1 + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_update_interact(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + expect(ids, times=1).interact('aid1') + + ids.update(name='aid1', typ='interact') + + verify(ids).interact('aid1') + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_update_rotate(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + expect(ids, times=1).rotate('aid1') + + ids.update(name='aid1', typ='rotate') + + verify(ids).rotate('aid1') + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_update_bad(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + from keri import kering + with pytest.raises(kering.KeriError): + ids.update(name='aid1', typ='basil') + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_delete(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + ids.delete(name='aid1') + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_interact_no_data(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + mock_hab = {'prefix': 'hab prefix', 'name': 'aid1', 'state': {'s': '0', 'd': 'hab digest'}} + expect(ids, times=1).get('aid1').thenReturn(mock_hab) + + from keri.core import eventing, serdering + mock_serder = mock({'ked': {'a': 'key event dictionary'}, 'raw': b'serder raw bytes'}, spec=serdering.SerderKERI, + strict=True) + expect(eventing, times=1).interact('hab prefix', sn=1, data=[None], dig='hab digest').thenReturn(mock_serder) + + mock_keeper = mock({'algo': 'salty', 'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + expect(mock_manager, times=1).get(aid=mock_hab).thenReturn(mock_keeper) + expect(mock_keeper, times=1).sign(ser=mock_serder.raw).thenReturn(['a signature']) + + expected_data = { + 'ixn': {'a': 'key event dictionary'}, + 'sigs': ['a signature'], + 'salty': {'keeper': 'params'} + } + from requests import Response + mock_response = mock({'json': lambda: {'success': 'yay'}}, spec=Response, strict=True) + expect(mock_client, times=1).put('/identifiers/aid1?type=ixn', json=expected_data).thenReturn(mock_response) + + ids.interact(name='aid1') + + verify(ids).get('aid1') + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_interact_with_data(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + mock_hab = {'prefix': 'hab prefix', 'name': 'aid1', 'state': {'s': '0', 'd': 'hab digest'}} + expect(ids, times=1).get('aid1').thenReturn(mock_hab) + + from keri.core import eventing, serdering + mock_serder = mock({'ked': {'a': 'key event dictionary'}, 'raw': b'serder raw bytes'}, spec=serdering.SerderKERI, + strict=True) + expect(eventing, times=1).interact('hab prefix', sn=1, data=[{'some': 'data'}, {'some': 'more'}], + dig='hab digest').thenReturn( + mock_serder) + + mock_keeper = mock({'algo': 'salty', 'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + expect(mock_manager, times=1).get(aid=mock_hab).thenReturn(mock_keeper) + expect(mock_keeper, times=1).sign(ser=mock_serder.raw).thenReturn(['a signature']) + + expected_data = { + 'ixn': {'a': 'key event dictionary'}, + 'sigs': ['a signature'], + 'salty': {'keeper': 'params'} + } + + from requests import Response + mock_response = mock({'json': lambda: {'success': 'yay'}}, spec=Response, strict=True) + expect(mock_client, times=1).put('/identifiers/aid1?type=ixn', json=expected_data).thenReturn(mock_response) + + ids.interact(name='aid1', data=[{'some': 'data'}, {'some': 'more'}]) + + verify(ids).get('aid1') + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_rotate(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + mock_hab = {'prefix': 'hab prefix', 'name': 'aid1', + 'state': {'s': '0', 'd': 'hab digest', 'b': ['wit1', 'wit2', 'wit3'], 'k': ['key1']}} + expect(ids, times=1).get('aid1').thenReturn(mock_hab) + + mock_keeper = mock({'algo': 'salty', 'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + expect(mock_manager, times=1).get(mock_hab).thenReturn(mock_keeper) + + keys = ['key1'] + ndigs = ['ndig1'] + expect(mock_keeper, times=1).rotate(ncodes=['A'], transferable=True, states=[{'i': 'state 1'}, {'i': 'state 2'}], + rstates=[{'i': 'rstate 1'}, {'i': 'rstate 2'}]).thenReturn((keys, ndigs)) + + from keri.core import serdering + mock_serder = mock({'ked': {'a': 'key event dictionary'}, 'raw': b'serder raw bytes'}, spec=serdering.SerderKERI, + strict=True) + + from keri.core import eventing + expect(eventing, times=1).rotate(pre='hab prefix', keys=['key1'], dig='hab digest', sn=1, isith='1', nsith='1', + ndigs=['ndig1'], toad=None, wits=['wit1', 'wit2', 'wit3'], + cuts=[], adds=[], data=[]).thenReturn(mock_serder) + + expect(mock_keeper, times=1).sign(ser=mock_serder.raw).thenReturn(['a signature']) + + from requests import Response + mock_response = mock(spec=Response, strict=True) + expected_data = {'rot': {'a': 'key event dictionary'}, 'sigs': ['a signature'], 'salty': {'keeper': 'params'}, + 'smids': ['state 1', 'state 2'], 'rmids': ['rstate 1', 'rstate 2']} + expect(mock_client, times=1).put('/identifiers/aid1', json=expected_data).thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn({'success': 'yay'}) + + _, _, out = ids.rotate(name='aid1', states=[{'i': 'state 1'}, {'i': 'state 2'}], + rstates=[{'i': 'rstate 1'}, {'i': 'rstate 2'}]) + assert out['success'] == 'yay' + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_add_end_role(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + mock_hab = {'prefix': 'hab prefix', 'name': 'aid1'} + expect(ids, times=1).get('aid1').thenReturn(mock_hab) + + from keri.core import serdering + mock_serder = mock({'ked': {'a': 'key event dictionary'}, 'raw': b'serder raw bytes'}, spec=serdering.SerderKERI, + strict=True) + expect(ids, times=1).makeEndRole('hab prefix', 'agent', None, None).thenReturn(mock_serder) + + from signify.core import keeping + mock_keeper = mock({'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + expect(mock_manager, times=1).get(aid=mock_hab).thenReturn(mock_keeper) + expect(mock_keeper, times=1).sign(ser=mock_serder.raw).thenReturn(['a signature']) + + from requests import Response + mock_response = mock(spec=Response, strict=True) + expected_data = {'rpy': {'a': 'key event dictionary'}, 'sigs': ['a signature']} + expect(mock_client, times=1).post('/identifiers/aid1/endroles', json=expected_data).thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn({'success': 'yay'}) + + serder, sig, out = ids.addEndRole('aid1') + assert serder == mock_serder + assert sig == ['a signature'] + assert out['success'] == 'yay' + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_sign(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + from keri.core import serdering + mock_serder = mock({'ked': {'a': 'key event dictionary'}, 'raw': b'serder raw bytes'}, spec=serdering.SerderKERI, + strict=True) + + mock_hab = {'prefix': 'hab prefix', 'name': 'aid1'} + expect(ids, times=1).get('aid1').thenReturn(mock_hab) + + from signify.core import keeping + mock_keeper = mock({'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + expect(mock_manager, times=1).get(aid=mock_hab).thenReturn(mock_keeper) + + expect(mock_keeper, times=1).sign(ser=mock_serder.raw).thenReturn(['signature 1', 'signature 2']) + + out = ids.sign('aid1', mock_serder) + + assert out == ['signature 1', 'signature 2'] + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_member(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.aiding import Identifiers + ids = Identifiers(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock(spec=Response, strict=True) + expect(mock_client, times=1).get('/identifiers/aid1/members').thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn({'success': 'yay'}) + + out = ids.members('aid1') + + assert out['success'] == 'yay' + + verifyNoUnwantedInteractions() + unstub() + + +def test_aiding_make_end_role(): + expected_data = {'cid': 'a prefix', 'role': 'witness', 'eid': 'an eid'} + + from keri.core import eventing + expect(eventing, times=1).reply(route='/end/role/add', data=expected_data, stamp='a timestamp') + + from signify.app.aiding import Identifiers + Identifiers.makeEndRole('a prefix', role='witness', eid='an eid', stamp='a timestamp') + + verifyNoUnwantedInteractions() + unstub() diff --git a/tests/app/test_challenging.py b/tests/app/test_challenging.py new file mode 100644 index 0000000..0820fe5 --- /dev/null +++ b/tests/app/test_challenging.py @@ -0,0 +1,111 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.test_challenging module + +Testing challenge with unit tests +""" + +from mockito import mock, verifyNoUnwantedInteractions, unstub, expect + + +def test_challenges_generate(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.challenging import Challenges + chas = Challenges(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock({}, spec=Response, strict=True) + expect(mock_client, times=1).get('/challenges').thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn( + {"words": ["word", "one", "two", "three"]} + ) + + out = chas.generate() + assert out == ["word", "one", "two", "three"] + + verifyNoUnwantedInteractions() + unstub() + + +def test_challenge_verify(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.challenging import Challenges + chas = Challenges(client=mock_client) # type: ignore + + name = "test" + source = "E123" + words = ["word", "one", "two", "three"] + from requests import Response + mock_response = mock({}, spec=Response, strict=True) + expect(mock_client, times=1).post(f'/challenges/{name}/verify/{source}', + json=dict(words=words)).thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn( + {"done": False} + ) + + out = chas.verify(name, source, words) + assert out["done"] is False + + verifyNoUnwantedInteractions() + unstub() + + +def test_challenge_responded(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.challenging import Challenges + chas = Challenges(client=mock_client) # type: ignore + + name = "test" + source = "E123" + said = "E456" + from requests import Response + mock_response = mock({}, spec=Response, strict=True) + expect(mock_client, times=1).put(f'/challenges/{name}/verify/{source}', + json=dict(said=said)).thenReturn(mock_response) + + out = chas.responded(name, source, said) + assert out is True + + verifyNoUnwantedInteractions() + unstub() + + +def test_challenge_respond(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.aiding import Identifiers + mock_ids = Identifiers(client=mock_client) # type: ignore + + from signify.peer.exchanging import Exchanges + mock_exc = Exchanges(client=mock_client) # type: ignore + + from signify.app.challenging import Challenges + chas = Challenges(client=mock_client) # type: ignore + + mock_hab = {} + name = "test" + recp = "E123" + words = ["word", "one", "two", "three"] + from requests import Response + mock_response = mock({}, spec=Response, strict=True) + expect(mock_client, times=1).identifiers().thenReturn(mock_ids) + expect(mock_ids, times=1).get(name).thenReturn(mock_hab) + expect(mock_client, times=1).exchanges().thenReturn(mock_exc) + expect(mock_exc, times=1).send(name, "challenge", sender=mock_hab, route="/challenge/response", + payload=dict(words=words), + embeds=dict(), + recipients=[recp]).thenReturn((None, None, mock_response)) + + out = chas.respond(name, recp, words) + assert out == mock_response + + verifyNoUnwantedInteractions() + unstub() diff --git a/tests/app/test_clienting.py b/tests/app/test_clienting.py index 907406c..742d2cd 100644 --- a/tests/app/test_clienting.py +++ b/tests/app/test_clienting.py @@ -1,332 +1,763 @@ # -*- encoding: utf-8 -*- """ SIGNIFY -signify.app.clienting module +signify.app.test_clienting module -Testing clienting with integration tests that require a running KERIA Cloud Agent +Testing clienting with unit tests """ -import os -from time import sleep -import requests -import responses +from mockito import mock, patch, unstub, verify, verifyNoUnwantedInteractions, expect import pytest -from keri import kering -from keri.app.cli.commands.witness import start as wstart -from keri.app.keeping import Algos -from keri.core.coring import Tiers, Serder - -from keria.app.cli.commands import start as kstart -from keria.testing.testing_helper import Helpers - -from signify.app.clienting import SignifyClient - -import threading - -TEST_DIR = os.path.dirname(os.path.abspath(__file__)) -cwd = os.getcwd() - -host="localhost" -adminport=3901 -httpport=3902 -bootport=3903 -agentPre = "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei" -ctrlPre = "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" -base="" -kname="keria" -kbran="" -wname="witness" -wbase="" -walias="witness" -wbran="" -wtcp=5631 -whttp=5632 -wexpire=0.0 -bran = "0123456789abcdefghijk" -burl = f"http://{host}:{bootport}/boot" -url = f"http://{host}:{adminport}" -configDir=f"{cwd}/tests/" -configFile="demo-witness-oobis.json" -wconfigDir=f"{cwd}/tests/" -wconfigFile="demo-witness-oobis.json" -wit1 = "BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha" -wit2 = "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM" -wit3 = "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX" - -@pytest.fixture -def setup(): - print("Before test", ) - Helpers.remove_test_dirs(ctrlPre) + +def test_signify_client_defaults(): + from signify.app.clienting import SignifyClient + patch(SignifyClient, 'connect', lambda str: None) + client = SignifyClient(passcode='abcdefghijklmnop01234', url='http://example.com') + + assert client.bran == 'abcdefghijklmnop01234' + assert client.pidx == 0 + from keri.core.coring import Tiers + assert client.tier == Tiers.low + assert client.extern_modules is None + + from signify.core.authing import Controller + assert isinstance(client.ctrl, Controller) + assert client.mgr is None + assert client.session is None + assert client.agent is None + assert client.authn is None + assert client.base is None + + verify(SignifyClient, times=1).connect('http://example.com') + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_bad_passcode_length(): + from keri import kering + with pytest.raises(kering.ConfigurationError, match='too short'): + from signify.app.clienting import SignifyClient + SignifyClient(passcode='too short') + +def test_signify_client_connect_no_delegation(): + from signify.core import authing + from keri.core.coring import Tiers + mock_init_controller = mock(spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_init_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + import requests + mock_session = mock(spec=requests.Session, strict=True) + expect(requests, times=1).Session().thenReturn(mock_session) + + from signify.signifying import State + mock_state = mock({'pidx': 0, 'agent': 'agent info', 'controller': 'controller info'}, spec=State, strict=True) + expect(client, times=1).states().thenReturn(mock_state) + + from signify.core import authing + mock_agent = mock({'delpre': 'a prefix'}, spec=authing.Agent, strict=True) + expect(authing, times=1).Agent(state=mock_state.agent).thenReturn(mock_agent) + + from keri.core import serdering + mock_serder = mock({'sn': 1}, spec=serdering.Serder, strict=True) + from keri.core import coring + mock_salter = mock(spec=coring.Salter, strict=True) + mock_controller = mock({'pre': 'a prefix', 'salter': mock_salter, 'serder': mock_serder}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low, state=mock_state.controller).thenReturn(mock_controller) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + expect(keeping, times=1).Manager(salter=mock_salter, extern_modules=None).thenReturn(mock_manager) + + from signify.core import authing + mock_authenticator = mock({'verify': lambda: {'hook1': 'hook1 info', 'hook2': 'hook2 info'}}, spec=authing.Authenticater, strict=True) + expect(authing, times=1).Authenticater(agent=mock_agent, ctrl=mock_controller).thenReturn(mock_authenticator) + + from signify.app import clienting + mock_signify_auth = mock(spec=clienting.SignifyAuth, strict=True) + expect(clienting, times=1).SignifyAuth(mock_authenticator).thenReturn(mock_signify_auth) + + client.connect('http://example.com') + + assert client.pidx == mock_state.pidx + assert client.session.auth == mock_signify_auth #type: ignore + assert client.session.hooks == {'response': mock_authenticator.verify} #type: ignore + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_connect_delegation(): + from signify.core import authing + from keri.core.coring import Tiers + mock_init_controller = mock(spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_init_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + import requests + mock_session = mock(spec=requests.Session, strict=True) + expect(requests, times=1).Session().thenReturn(mock_session) + + from signify.signifying import State + mock_state = mock({'pidx': 0, 'agent': 'agent info', 'controller': 'controller info'}, spec=State, strict=True) + expect(client, times=1).states().thenReturn(mock_state) + + from signify.core import authing + mock_agent = mock({'delpre': 'a prefix'}, spec=authing.Agent, strict=True) + expect(authing, times=1).Agent(state=mock_state.agent).thenReturn(mock_agent) + + from keri.core import serdering + mock_serder = mock({'sn': 0}, spec=serdering.Serder, strict=True) + from keri.core import coring + mock_salter = mock(spec=coring.Salter, strict=True) + mock_controller = mock({'pre': 'a prefix', 'salter': mock_salter, 'serder': mock_serder}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low, state=mock_state.controller).thenReturn(mock_controller) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + expect(keeping, times=1).Manager(salter=mock_salter, extern_modules=None).thenReturn(mock_manager) + + expect(client, times=1).approveDelegation() + + from signify.core import authing + mock_authenticator = mock({'verify': lambda: {'hook1': 'hook1 info', 'hook2': 'hook2 info'}}, spec=authing.Authenticater, strict=True) + expect(authing, times=1).Authenticater(agent=mock_agent, ctrl=mock_controller).thenReturn(mock_authenticator) + + from signify.app import clienting + mock_signify_auth = mock(spec=clienting.SignifyAuth, strict=True) + expect(clienting, times=1).SignifyAuth(mock_authenticator).thenReturn(mock_signify_auth) + + client.connect('http://example.com') + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_connect_bad_scheme(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + from keri.kering import ConfigurationError + with pytest.raises(ConfigurationError, match='invalid scheme foo for SignifyClient'): + client.connect('foo://example.com') + +def test_signify_client_connect_bad_delegation(): + from signify.core import authing + from keri.core.coring import Tiers + mock_init_controller = mock(spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_init_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + import requests + mock_session = mock(spec=requests.Session, strict=True) + expect(requests, times=1).Session().thenReturn(mock_session) + + from signify.signifying import State + mock_state = mock({'pidx': 0, 'agent': 'agent info', 'controller': 'controller info'}, spec=State, strict=True) + expect(client, times=1).states().thenReturn(mock_state) + + from signify.core import authing + mock_agent = mock({'delpre': 'a prefix'}, spec=authing.Agent, strict=True) + expect(authing, times=1).Agent(state=mock_state.agent).thenReturn(mock_agent) + + from keri.core import serdering + mock_serder = mock({'sn': 1}, spec=serdering.Serder, strict=True) + from keri.core import coring + mock_salter = mock(spec=coring.Salter, strict=True) + mock_controller = mock({'pre': 'a different prefix', 'salter': mock_salter, 'serder': mock_serder}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low, state=mock_state.controller).thenReturn(mock_controller) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + expect(keeping, times=1).Manager(salter=mock_salter, extern_modules=None).thenReturn(mock_manager) + + from keri.kering import ConfigurationError + with pytest.raises(ConfigurationError, match='commitment to controller AID missing in agent inception event'): + client.connect('https://example.com') + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_approve_delegation(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) - # Start witness network - wThread=threading.Thread(target=wstart.runWitness, - args=[wname, - wbase, - walias, - wbran, - wtcp, - whttp, - 0.0, - wconfigDir, - wconfigFile]) - wThread.daemon=True - wThread.start() + from signify.core import authing + mock_agent = mock({'delpre': 'a prefix'}, spec=authing.Agent, strict=True) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.agent = mock_agent # type: ignore + + from keri.core import serdering + mock_serder = mock({'ked': 'key event dictionary'}, spec=serdering.Serder, strict=True) + signatures = ["signature 1", "signature 2"] + expect(client.ctrl, times=1).approveDelegation(mock_agent).thenReturn((mock_serder, signatures)) + + expected_data = {'ixn': 'key event dictionary', 'sigs': ['signature 1', 'signature 2']} + expect(client, times=1).put(path="/agent/a_prefix?type=ixn", json=expected_data) + + client.approveDelegation() + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_rotate(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + expect(mock_controller, times=1).rotate(nbran="new bran", aids=["aid1", "aid2"]).thenReturn({'rotate': 'data'}) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + expect(client, times=1).put(path="/agent/a_prefix", json={'rotate': 'data'}) + + client.rotate("new bran", ["aid1", "aid2"]) + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_properties(): + from keri.core import serdering + mock_serder = mock(spec=serdering.Serder, strict=True) + + from keri.core import coring + mock_salter = mock(spec=coring.Salter, strict=True) + + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({ + 'pre': 'a_prefix', + 'serder': mock_serder, + 'salter': mock_salter + }, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + client.mgr = mock_manager # type: ignore + + assert client.controller == "a_prefix" + assert client.icp == mock_serder + assert client.salter == mock_salter + assert client.manager == mock_manager + + verifyNoUnwantedInteractions() + unstub() + +@pytest.mark.parametrize("data,expected_pidx", [ + ({'controller': 'controller info', 'agent': 'agent info'}, 0), + ({'controller': 'controller info', 'agent': 'agent info', 'pidx': 1}, 1), +]) +def test_signify_client_states(data, expected_pidx): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'status_code': 200}, spec=requests.Response, strict=True) + expect(mock_session, times=1).get(url='http://example.com/agent/a_prefix').thenReturn(mock_response) + + expect(mock_response, times=1).json().thenReturn(data) + + states = client.states() + + assert states.controller == 'controller info' + assert states.agent == 'agent info' + assert states.pidx == expected_pidx + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_states_agent_error(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'status_code': 404}, spec=requests.Response, strict=True) + expect(mock_session, times=1).get(url='http://example.com/agent/a_prefix').thenReturn(mock_response) + + from keri import kering + with pytest.raises(kering.ConfigurationError, match='agent does not exist for controller a_prefix'): + client.states() + + unstub() + verifyNoUnwantedInteractions() + +@pytest.mark.parametrize("status_code,expected", [ + (200, False), + (204, True), + (500, False), + (400, False), +]) +def test_signify_client_save_old_salt(status_code, expected): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') - # Start keria cloud agent - kThread=threading.Thread(target=kstart.runAgent, - args=[kname, - base, - kbran, - adminport, - httpport, - bootport, - configFile, - configDir, - 0.0]) - kThread.daemon=True - kThread.start() - -@pytest.fixture -def teardown(): - print("After test") + import requests + mock_response = mock({'status_code': status_code}, spec=requests.Response, strict=True) + + expected_data = {'salt': 'salty'} + expect(client, times=1).put('/salt/a_prefix', json=expected_data).thenReturn(mock_response) -def test_init(setup,teardown): - # Try with bran that is too short - with pytest.raises(kering.ConfigurationError): - SignifyClient(passcode=bran[:16], tier=Tiers.low) - - # Try with an invalid URL - with pytest.raises(kering.ConfigurationError): - SignifyClient(url="ftp://www.example.com", passcode=bran, tier=Tiers.low) - - client = SignifyClient(passcode=bran) - assert client.controller == ctrlPre - - tier = Tiers.low - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller == ctrlPre - - tier = Tiers.med - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller != ctrlPre - - tier = Tiers.high - client = SignifyClient(passcode=bran, tier=tier) - assert client.controller != ctrlPre - -def test_connect(setup,teardown): - client = SignifyClient(passcode=bran, tier=Tiers.low) - assert client.controller == ctrlPre - - evt, siger = client.ctrl.event() - - print(evt.pretty()) - print(siger.qb64) - res = requests.post(url=burl, - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.pre == agentPre - assert client.agent.delpre == ctrlPre - - identifiers = client.identifiers() - aids = identifiers.list() - assert aids == [] - - op1 = identifiers.create("aid1", bran=f"{bran}_1") - aid = op1["response"] - icp = Serder(ked=aid) - assert icp.pre == "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK" - assert len(icp.verfers) == 1 - assert icp.verfers[0].qb64 == "DPmhSfdhCPxr3EqjxzEtF8TVy0YX7ATo0Uc8oo2cnmY9" - assert len(icp.digers) == 1 - assert icp.digers[0].qb64 == "EAORnRtObOgNiOlMolji-KijC_isa3lRDpHCsol79cOc" - assert icp.tholder.num == 1 - assert icp.ntholder.num == 1 - - rpy = identifiers.makeEndRole(pre=icp.pre, eid="EPGaq6inGxOx-VVVEcUb_KstzJZldHJvVsHqD4IPxTWf") - print(rpy.pretty()) - assert rpy.ked['a']['cid'] == "ELUvZ8aJEHAQE-0nsevyYTP98rBbGJUrTj5an-pCmwrK" - assert rpy.ked['a']['eid'] == "EPGaq6inGxOx-VVVEcUb_KstzJZldHJvVsHqD4IPxTWf" - - aids = identifiers.list() - assert len(aids) == 1 - aid = aids.pop() - - salt = aid[Algos.salty] - assert aid['name'] == "aid1" - assert salt["pidx"] == 0 - assert aid["prefix"] == icp.pre - assert salt["stem"] == "signify:aid" - - op2 = identifiers.create("aid2", count=3, ncount=3, isith="2", nsith="2", bran="0123456789lmnopqrstuv") - aid2 = op2["response"] - icp2 = Serder(ked=aid2) - print(icp2.pre) - assert icp2.pre == "EP10ooRj0DJF0HWZePEYMLPl-arMV-MAoTKK-o3DXbgX" - assert len(icp2.verfers) == 3 - assert icp2.verfers[0].qb64 == "DGBw7C7AfC7jbD3jLLRS3SzIWFndM947TyNWKQ52iQx5" - assert icp2.verfers[1].qb64 == "DD_bHYFsgWXuCbz3SD0HjCIe_ITjRvEoCGuZ4PcNFFDz" - assert icp2.verfers[2].qb64 == "DEe9u8k0fm1wMFAuOIsCtCNrpduoaV5R21rAcJl0awze" - assert len(icp2.digers) == 3 - print([diger.qb64 for diger in icp2.digers]) - assert icp2.digers[0].qb64 == "EML5FrjCpz8SEl4dh0U15l8bMRhV_O5iDcR1opLJGBSH" - assert icp2.digers[1].qb64 == "EJpKquuibYTqpwMDqEFAFs0gwq0PASAHZ_iDmSF3I2Vg" - assert icp2.digers[2].qb64 == "ELplTAiEKdobFhlf-dh1vUb2iVDW0dYOSzs1dR7fQo60" - assert icp2.tholder.num == 2 - assert icp2.ntholder.num == 2 - - aids = identifiers.list() - assert len(aids) == 2 - aid = aids[1] - assert aid['name'] == "aid2" - assert aid["prefix"] == icp2.pre - salt = aid[Algos.salty] - assert salt["pidx"] == 1 - assert salt["stem"] == "signify:aid" - - op3 = identifiers.rotate("aid1") - ked = op3["response"] - rot = Serder(ked=ked) - - assert rot.said == "EBQABdRgaxJONrSLcgrdtbASflkvLxJkiDO0H-XmuhGg" - assert rot.sn == 1 - assert len(rot.digers) == 1 - assert rot.verfers[0].qb64 == "DHgomzINlGJHr-XP3sv2ZcR9QsIEYS3LJhs4KRaZYKly" - assert rot.digers[0].qb64 == "EJMovBlrBuD6BVeUsGSxLjczbLEbZU9YnTSud9K4nVzk" - - op4 = identifiers.interact("aid1", data=[icp.pre]) - ked = op4["response"] - ixn = Serder(ked=ked) - assert ixn.said == "ENsmRAg_oM7Hl1S-GTRMA7s4y760lQMjzl0aqOQ2iTce" - assert ixn.sn == 2 - assert ixn.ked["a"] == [icp.pre] - - aid = identifiers.get("aid1") - state = aid["state"] - assert state['s'] == '2' - assert state['f'] == '2' - assert state['et'] == 'ixn' - assert state['d'] == ixn.said - assert state['ee']['d'] == rot.said - - events = client.keyEvents() - log = events.get(pre=aid["prefix"]) - assert len(log) == 3 - serder = Serder(ked=log[0]) - assert serder.pre == icp.pre - assert serder.said == icp.said - serder = Serder(ked=log[1]) - assert serder.pre == rot.pre - assert serder.said == rot.said - serder = Serder(ked=log[2]) - assert serder.pre == ixn.pre - assert serder.said == ixn.said - - print(identifiers.list()) - -def test_witnesses(setup,teardown): - client = SignifyClient(passcode=bran, tier=Tiers.low) - assert client.controller == ctrlPre - evt, siger = client.ctrl.event() - res = requests.post(url=burl, - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) + assert client._save_old_salt("salty") == expected + + verifyNoUnwantedInteractions() + unstub() + +@pytest.mark.parametrize("status_code,expected", [ + (200, False), + (204, True), + (500, False), + (400, False), +]) +def test_signify_client_delete_old_salt(status_code, expected): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") + import requests + mock_response = mock({'status_code': status_code}, spec=requests.Response, strict=True) + + expect(client, times=1).delete('/salt/a_prefix').thenReturn(mock_response) - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == ctrlPre - assert client.agent.pre == agentPre - - identifiers = client.identifiers() - operations = client.operations() - - # Use witnesses - op = identifiers.create("aid1", bran="canIGetAWitnessSaltGreaterThan21", toad="2", - wits=[wit1, wit2, wit3]) - - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - icp1 = Serder(ked=op["response"]) - assert icp1.pre == "EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa" - assert icp1.ked['b'] == [wit1, - wit2, - wit3] - assert icp1.ked['bt'] == "2" - - aid1 = identifiers.get("aid1") - assert aid1["prefix"] == icp1.pre - assert len(aid1["windexes"]) == 3 - - aids = identifiers.list() - assert len(aids) == 1 - aid = aids.pop() - assert aid['prefix'] == icp1.pre - - -def test_delegation(setup,teardown): - client = SignifyClient(passcode=bran, tier=Tiers.low) - print(client.controller) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - evt, siger = client.ctrl.event() - res = requests.post(url="http://localhost:3903/boot", - json=dict( - icp=evt.ked, - sig=siger.qb64, - stem=client.ctrl.stem, - pidx=1, - tier=client.ctrl.tier)) - - if res.status_code != requests.codes.accepted: - raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}") - - client.connect(url=url) - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - # Delegator OOBI: - # http://127.0.0.1:5642/oobi/EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7/witness - - delpre = "EHpD0-CDWOdu5RJ8jHBSUkOqBZ3cXeDVHWNb_Ul89VI7" - identifiers = client.identifiers() - operations = client.operations() - oobis = client.oobis() - - op = oobis.resolve(f"http://127.0.0.1:5642/oobi/{delpre}/witness") - print("OOBI op is: ", op) - - count = 0 - while not op["done"] and not count > 25: - op = operations.get(op["name"]) - sleep(1) - - op = identifiers.create("aid1", toad="2", delpre=delpre, wits=[wit1, wit2, wit3]) - pre = op["metadata"]["pre"] - - while not op["done"]: - op = operations.get(op["name"]) - sleep(1) - - icp1 = Serder(ked=op["response"]) - - print(icp1.pretty()) - assert icp1.pre == pre + assert client._delete_old_salt() == expected + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_get(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'ok': True}, spec=requests.Response, strict=True) + expect(mock_session).get('http://example.com/my_path', params={'a': 'param'}, headers={'a': 'header'}, json={'a': 'body'}).thenReturn(mock_response) + + out = client.get('my_path', params={'a': 'param'}, headers={'a': 'header'}, body={'a': 'body'}) + assert out == mock_response + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_get_not_ok(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'ok': False}, spec=requests.Response, strict=True) + from mockito import kwargs + expect(mock_session).get('http://example.com/my_path', **kwargs).thenReturn(mock_response) + + expect(client, times=1).raiseForStatus(mock_response) + client.get('my_path', params={'a': 'param'}, headers={'a': 'header'}, body={'a': 'body'}) + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_stream(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + from mockito import kwargs + import sseclient + expect(sseclient, times=1).SSEClient('http://example.com/my_path', session=mock_session, **kwargs).thenReturn([]) + + # probably a cleaner way to do this + with pytest.raises(StopIteration): + next(client.stream('my_path', params={'a': 'param'}, headers={'a': 'header'}, body={'a': 'body'})) + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_delete(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'ok': True}, spec=requests.Response, strict=True) + from mockito import kwargs + expect(mock_session).delete('http://example.com/my_path', params={'a': 'param'}, headers={'a': 'header'}).thenReturn(mock_response) + + out = client.delete('my_path', params={'a': 'param'}, headers={'a': 'header'}) + assert out == mock_response + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_delete_not_ok(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'ok': False}, spec=requests.Response, strict=True) + from mockito import kwargs + expect(mock_session).delete('http://example.com/my_path', **kwargs).thenReturn(mock_response) + + expect(client, times=1).raiseForStatus(mock_response) + client.delete('my_path', params={'a': 'param'}, headers={'a': 'header'}) + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_post(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'ok': True}, spec=requests.Response, strict=True) + expect(mock_session).post('http://example.com/my_path', json={'a': 'json'}, params={'a': 'param'}, headers={'a': 'header'}).thenReturn(mock_response) + + json = {'a': 'json'} + out = client.post('my_path', json, params={'a': 'param'}, headers={'a': 'header'}) + assert out == mock_response + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_post_not_ok(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'ok': False}, spec=requests.Response, strict=True) + from mockito import kwargs + expect(mock_session).post('http://example.com/my_path', **kwargs).thenReturn(mock_response) + expect(client, times=1).raiseForStatus(mock_response) + + json = {'a': 'json'} + client.post('my_path', json, params={'a': 'param'}, headers={'a': 'header'}) + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_put(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'ok': True}, spec=requests.Response, strict=True) + expect(mock_session).put('http://example.com/my_path', json={'a': 'json'}, params={'a': 'param'}, headers={'a': 'header'}).thenReturn(mock_response) + + json = {'a': 'json'} + out = client.put('my_path', json, params={'a': 'param'}, headers={'a': 'header'}) + assert out == mock_response + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_put_not_ok(): + from signify.core import authing + from keri.core.coring import Tiers + mock_controller = mock({'pre': 'a_prefix'}, spec=authing.Controller, strict=True) + expect(authing, times=1).Controller(bran='abcdefghijklmnop01234', tier=Tiers.low).thenReturn(mock_controller) + + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + client.base = 'http://example.com' + + import requests + mock_session = mock(spec=requests.Session, strict=True) + client.session = mock_session # type: ignore + + mock_response = mock({'ok': False}, spec=requests.Response, strict=True) + from mockito import kwargs + expect(mock_session).put('http://example.com/my_path', **kwargs).thenReturn(mock_response) + expect(client, times=1).raiseForStatus(mock_response) + + json = {'a': 'json'} + client.put('my_path', json, params={'a': 'param'}, headers={'a': 'header'}) + + verifyNoUnwantedInteractions() + unstub() + +def test_signify_client_identfiers(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.identifiers() + + from signify.app.aiding import Identifiers + assert type(out) is Identifiers + assert out.client == client + +def test_signify_client_operations(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.operations() + + from signify.app.coring import Operations + assert type(out) is Operations + assert out.client == client + +def test_signify_client_oobis(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.oobis() + + from signify.app.coring import Oobis + assert type(out) is Oobis + assert out.client == client + +def test_signify_client_credentials(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.credentials() + + from signify.app.credentialing import Credentials + assert type(out) is Credentials + assert out.client == client + +def test_signify_client_key_states(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.keyStates() + + from signify.app.coring import KeyStates + assert type(out) is KeyStates + assert out.client == client + +def test_signify_client_key_events(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.keyEvents() + + from signify.app.coring import KeyEvents + assert type(out) is KeyEvents + assert out.client == client + + +def test_signify_client_escrows(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.escrows() + + from signify.app.escrowing import Escrows + assert type(out) is Escrows + assert out.client == client + + +def test_signify_client_endroles(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.endroles() + + from signify.app.ending import EndRoleAuthorizations + assert type(out) is EndRoleAuthorizations + assert out.client == client + + +def test_signify_client_notifications(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.notifications() + + from signify.app.notifying import Notifications + assert type(out) is Notifications + assert out.client == client + + +def test_signify_client_groups(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.groups() + + from signify.app.grouping import Groups + assert type(out) is Groups + assert out.client == client + + +def test_signify_client_registries(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.registries() + + from signify.app.credentialing import Registries + assert type(out) is Registries + assert out.client == client + + +def test_signify_client_exchanges(): + from signify.app.clienting import SignifyClient + client = SignifyClient(passcode='abcdefghijklmnop01234') + + out = client.exchanges() + + from signify.peer.exchanging import Exchanges + assert type(out) is Exchanges + assert out.client == client + + +@pytest.mark.parametrize("resp,err", [ + ({'json': lambda : {'description': {'raise a description'}}, 'status_code': 400, 'url': 'http://example.com'}, "400 Client Error: {'raise a description'} for url: http://example.com"), + ({'json': lambda : {'title': {'raise a title'}}, 'status_code': 400, 'url': 'http://example.com'}, "400 Client Error: {'raise a title'} for url: http://example.com"), + ({'json': lambda : {'unknown': {'raise unknown'}}, 'status_code': 400, 'url': 'http://example.com'}, "400 Client Error: Unknown for url: http://example.com"), + ({'json': lambda : {'description': {'raise a description'}}, 'status_code': 500, 'url': 'http://example.com'}, "500 Server Error: {'raise a description'} for url: http://example.com"), + ({'json': lambda : {'title': {'raise a title'}}, 'status_code': 500, 'url': 'http://example.com'}, "500 Server Error: {'raise a title'} for url: http://example.com"), + ({'json': lambda : {'unknown': {'raise unknown'}}, 'status_code': 500, 'url': 'http://example.com'}, "500 Server Error: Unknown for url: http://example.com"), + ({'text': 'a text error', 'status_code': 400, 'url': 'http://example.com'}, "400 Client Error: a text error for url: http://example.com"), + ({'text': 'a text error', 'status_code': 500, 'url': 'http://example.com'}, "500 Server Error: a text error for url: http://example.com"), +]) +def test_signify_client_raise_for_status(resp, err): + import requests + mock_response = mock(resp, spec=requests.Response) + + from signify.app.clienting import SignifyClient + + with pytest.raises(requests.HTTPError, match=err): + SignifyClient.raiseForStatus(mock_response) + + unstub() + verifyNoUnwantedInteractions() + +def test_signify_auth(): + from signify.core import authing + mock_agent = mock(spec=authing.Agent, strict=True) + mock_controller = mock({'pre': 'a prefix'}, spec=authing.Controller, strict=True) + + from signify.core import authing + mock_authenticator = mock({'ctrl': mock_controller}, spec=authing.Authenticater, strict=True) + expect(authing, times=1).Authenticater(agent=mock_agent, ctrl=mock_controller).thenReturn(mock_authenticator) + + from signify.app.clienting import SignifyAuth + signify_auth = SignifyAuth(mock_authenticator) + + import requests + mock_request = mock({'method': 'GET', 'url': 'http://example.com/my_path', 'headers': {}, 'body': "a body for len"}, spec=requests.Request, strict=True) + + from keri.help import helping + expect(helping).nowIso8601().thenReturn('now ISO8601!') + + expected_headers = { + 'Signify-Resource': 'a prefix', + 'Signify-Timestamp': 'now ISO8601!', + 'Content-Length': 11 + } + expect(mock_authenticator, times=1).sign({'Signify-Resource': 'a prefix', 'Signify-Timestamp': 'now ISO8601!', 'Content-Length': 14}, 'GET', '/my_path').thenReturn({'headers': 'modified'}) + + out = signify_auth.__call__(mock_request) + assert out.headers == {'headers': 'modified'} + + unstub() + verifyNoUnwantedInteractions() diff --git a/tests/app/test_contacting.py b/tests/app/test_contacting.py new file mode 100644 index 0000000..b442ce0 --- /dev/null +++ b/tests/app/test_contacting.py @@ -0,0 +1,33 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.test_contacting module + +Testing contacting with unit tests +""" + +from mockito import mock, unstub, expect, verifyNoUnwantedInteractions + + +def test_contact_list(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient) + + from signify.app.contacting import Contacts + contacts = Contacts(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock({'headers': {'content-range': 'contents 0-10/20'}}, spec=Response, strict=True) + expect(mock_client, times=1).get('/contacts', headers=dict(Range=f"contacts={0}-{24}")).thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn( + ['contact1', 'contact2'] + ) + + out = contacts.list() + assert out['start'] == 0 + assert out['end'] == 2 + assert out['total'] == 2 + assert out['contacts'] == ['contact1', 'contact2'] + + verifyNoUnwantedInteractions() + unstub() diff --git a/tests/app/test_coring.py b/tests/app/test_coring.py new file mode 100644 index 0000000..3a03a7a --- /dev/null +++ b/tests/app/test_coring.py @@ -0,0 +1,133 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.test_coring module + +Testing coring with unit tests +""" + +from mockito import mock, expect, unstub, verifyNoUnwantedInteractions + + +def test_operations(): + from signify.app.clienting import SignifyClient + client = mock(spec=SignifyClient, strict=True) + + from signify.app import coring + ops = coring.Operations(client=client) # type: ignore + + import requests + mock_response = mock(spec=requests.Response, strict=True) + expect(client, times=1).get('/operations/a_name').thenReturn(mock_response) + + expect(mock_response, times=1).json().thenReturn({'some': 'json'}) + + ops.get("a_name") + + verifyNoUnwantedInteractions() + unstub() + +def test_oobis_get(): + from signify.app.clienting import SignifyClient + client = mock(spec=SignifyClient, strict=True) + + from signify.app import coring + oobis = coring.Oobis(client=client) # type: ignore + + import requests + mock_response = mock(spec=requests.Response) + expect(client, times=1).get('/identifiers/a_name/oobis?role=my_role').thenReturn(mock_response) + + expect(mock_response, times=1).json().thenReturn({'some': 'json'}) + + oobis.get("a_name", "my_role") + + verifyNoUnwantedInteractions() + unstub() + +def test_oobis_resolve(): + from signify.app.clienting import SignifyClient + client = mock(spec=SignifyClient, strict=True) + + from signify.app import coring + oobis = coring.Oobis(client=client) # type: ignore + + import requests + mock_response = mock(spec=requests.Response, strict=True) + expect(client, times=1).post('/oobis', json={'url': 'my oobi', 'oobialias': 'Harry'}).thenReturn(mock_response) + + expect(mock_response, times=1).json().thenReturn({'some': 'json'}) + + oobis.resolve("my oobi", alias="Harry") + + verifyNoUnwantedInteractions() + unstub() + +def test_key_states_get(): + from signify.app.clienting import SignifyClient + client = mock(spec=SignifyClient, strict=True) + + from signify.app import coring + ks = coring.KeyStates(client=client) # type: ignore + + import requests + mock_response = mock(spec=requests.Response, strict=True) + expect(client, times=1).get('/states?pre=a_prefix').thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn({'some': 'json'}) + + ks.get("a_prefix") + + verifyNoUnwantedInteractions() + unstub() + +def test_key_states_list(): + from signify.app.clienting import SignifyClient + client = mock(spec=SignifyClient, strict=True) + + from signify.app import coring + ks = coring.KeyStates(client=client) # type: ignore + + import requests + mock_response = mock(spec=requests.Response, strict=True) + expect(client, times=1).get('/states?pre=pre1&pre=pre2').thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn({'some': 'json'}) + + ks.list(["pre1", "pre2"]) + + verifyNoUnwantedInteractions() + unstub() + +def test_key_states_query(): + from signify.app.clienting import SignifyClient + client = mock(spec=SignifyClient, strict=True) + + from signify.app import coring + ks = coring.KeyStates(client=client) # type: ignore + + import requests + mock_response = mock(spec=requests.Response, strict=True) + + expect(client, times=1).post('/queries', json={'pre': 'a_prefix', 'sn': 0, 'anchor': {'my': 'anchor'}}).thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn({'some': 'json'}) + + ks.query("a_prefix", sn=0, anchor={'my': 'anchor'}) + + verifyNoUnwantedInteractions() + unstub() + +def test_key_events(): + from signify.app.clienting import SignifyClient + client = mock(spec=SignifyClient, strict=True) + + from signify.app import coring + ke = coring.KeyEvents(client=client) # type: ignore + + import requests + mock_response = mock(spec=requests.Response, strict=True) + expect(client, times=1).get('/events?pre=my_prefix').thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn({'some': 'json'}) + + ke.get("my_prefix") + + verifyNoUnwantedInteractions() + unstub() diff --git a/tests/app/test_credentialing.py b/tests/app/test_credentialing.py new file mode 100644 index 0000000..1c0218e --- /dev/null +++ b/tests/app/test_credentialing.py @@ -0,0 +1,279 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.test_credentialing module + +Testing credentialing with unit tests +""" +from keri.peer import exchanging +from keri.vdr import eventing as veventing +from keri.core import eventing, coring +from mockito import mock, unstub, verify, verifyNoUnwantedInteractions, expect, ANY + +from signify.app import credentialing + + +def test_registries(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.aiding import Identifiers + mock_ids = mock(spec=Identifiers, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from requests import Response + mock_response = mock({'json': lambda: {}}, spec=Response, strict=True) + mock_hab = {'prefix': 'ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose', + 'name': 'aid1', 'state': {'s': '1', 'd': "ABCDEFG"}} + name = "aid1" + regName = "reg1" + + expect(mock_client, times=1).identifiers().thenReturn(mock_ids) + expect(mock_ids, times=1).get(name).thenReturn(mock_hab) + + mock_keeper = mock({'algo': 'salty', 'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + expect(mock_manager, times=2).get(aid=mock_hab).thenReturn(mock_keeper) + expect(mock_keeper, times=1).sign(ser=ANY()).thenReturn(['a signature']) + expect(mock_client, times=1).post(path=f"/identifiers/{name}/registries", json=ANY()).thenReturn(mock_response) + + from signify.app.credentialing import Registries + + registries = Registries(client=mock_client) + registries.create(hab=mock_hab, registryName=regName) + + expect(mock_client, times=1).get(f"/identifiers/{name}/registries/{regName}").thenReturn(mock_response) + registries.get(name="aid1", registryName=regName) + + pre = "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" + dig = "EOgQvKz8ziRn7FdR_ebwK9BkaVOnGeXQOJ87N6hMLrK0" + nonce = "ACb_3pGwW3uIjtOg4zRQ66I-SggMcmoyju_uCzuSvgG4" + serder = veventing.incept(pre=pre, nonce=nonce) + anc = eventing.interact(pre=pre, dig=dig) + + msg = Registries.serialize(serder, anc) + assert msg == (b'{"v":"KERI10JSON00010f_","t":"vcp","d":"EGaypC6sODRFyIuhdFzzFmBU' + b'4Xe5SNprALGbltnyHYSz","i":"EGaypC6sODRFyIuhdFzzFmBU4Xe5SNprALGbl' + b'tnyHYSz","ii":"ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose","s"' + b':"0","c":[],"bt":"0","b":[],"n":"ACb_3pGwW3uIjtOg4zRQ66I-SggMcmo' + b'yju_uCzuSvgG4"}-VAS-GAB0AAAAAAAAAAAAAAAAAAAAAABENns5-voIbnRMADUO' + b'so7HDiQ9ZS_AfU8BfgGLHEW54H1') + + unstub() + + +def test_credentials_list(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from requests import Response + mock_response = mock({'json': lambda: {}}, spec=Response, strict=True) + expect(mock_client, times=1).post('/credentials/query', + json={'filter': {'genre': 'horror'}, + 'sort': ['updside down'], 'skip': 10, 'limt': 10}).thenReturn(mock_response) + + from signify.app.credentialing import Credentials + Credentials(client=mock_client).list(filtr={'genre': 'horror'}, sort=['updside down'], skip=10, + limit=10) # type: ignore + + verify(mock_response, times=1).json() + + verifyNoUnwantedInteractions() + unstub() + + +def test_credentials_export(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from requests import Response + mock_response = mock({'content': 'things I found'}, spec=Response, strict=True) + expect(mock_client, times=1).get('/identifiers/aid1/credentials/a_said', + headers={'accept': 'application/json+cesr'}).thenReturn(mock_response) + + from signify.app.credentialing import Credentials + out = Credentials(client=mock_client).export('aid1', 'a_said') # type: ignore + + assert out == 'things I found' + + verifyNoUnwantedInteractions() + unstub() + + +def test_credentials_create(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + mock_hab = {'prefix': 'ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose', 'name': 'aid1', + 'state': {'s': '1', 'd': "ABCDEFG"}} + mock_registry = {'regk': "EKRg7i8jS4O6BYUYiQG7X8YiMYdDXdw28tJRhFndCdGF", + 'pre': 'EHpwssa6tmD2U5W7-aogym-r1NobKBNXydP4MmaebA4O', 'state': {'c': ['NB']}} + data = dict(dt="2023-09-27T16:27:14.376928+00:00", LEI="ABC1234567890AD4456") + schema = "EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao" + recp = "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" + + mock_keeper = mock({'algo': 'salty', 'params': lambda: {'keeper': 'params'}}, spec=keeping.SaltyKeeper, strict=True) + expect(mock_manager, times=2).get(aid=mock_hab).thenReturn(mock_keeper) + expect(mock_keeper, times=1).sign(ser=ANY()).thenReturn(['a signature']) + from requests import Response + mock_response = mock({}, spec=Response, strict=True) + expect(mock_response, times=1).json().thenReturn({'v': 'ACDC10JSON00014c_'}) + + sad = {'v': 'ACDC10JSON00014c_', 'd': '', + 'i': 'ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose', + 'ri': 'a_regk', 's': 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao', + 'a': {'d': 'EHpwssa6tmD2U5W7-aogym-r1NobKBNXydP4MmaebA4O', + 'i': 'ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose', + 'dt': '2023-09-27T16:27:14.376928+00:00', 'LEI': 'ABC1234567890AD4456'}} + + _, sad = coring.Saider.saidify(sad) + + body = {'acdc': {'v': 'ACDC10JSON000196_', 'd': 'EK2xYrVkfJJHvlGhP79sfEPvQGmkFPPNAj-bjI5oHy7m', + 'i': 'EHpwssa6tmD2U5W7-aogym-r1NobKBNXydP4MmaebA4O', + 'ri': 'EKRg7i8jS4O6BYUYiQG7X8YiMYdDXdw28tJRhFndCdGF', + 's': 'EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao', + 'a': {'d': 'EHpwssa6tmD2U5W7-aogym-r1NobKBNXydP4MmaebA4O', + 'i': 'ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose', + 'dt': '2023-09-27T16:27:14.376928+00:00', 'LEI': 'ABC1234567890AD4456'}}, + 'iss': {'v': 'KERI10JSON0000ed_', 't': 'iss', 'd': 'EE8yncw1LCyBVtZPtozAFi7qvGn9dRPwTbuq--ulOAtB', + 'i': 'EK2xYrVkfJJHvlGhP79sfEPvQGmkFPPNAj-bjI5oHy7m', 's': '0', + 'ri': 'EKRg7i8jS4O6BYUYiQG7X8YiMYdDXdw28tJRhFndCdGF', 'dt': '2023-09-27T16:27:14.376928+00:00'}, + 'ixn': {'v': 'KERI10JSON000115_', 't': 'ixn', 'd': 'EC5KxyucpxnOpIpHe2QUPs9YeH1yGvkALg8NcWLYFe6a', + 'i': 'ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose', 's': '2', 'p': 'ABCDEFG', 'a': [ + {'i': 'EK2xYrVkfJJHvlGhP79sfEPvQGmkFPPNAj-bjI5oHy7m', 's': '0', + 'd': 'EE8yncw1LCyBVtZPtozAFi7qvGn9dRPwTbuq--ulOAtB'}]}, 'sigs': ['a signature'], + 'salty': {'keeper': 'params'}} + + expect(mock_client, times=1).post(f"/identifiers/aid1/credentials", json=body).thenReturn(mock_response) + + from signify.app.credentialing import Credentials + creder, iss, ixn, sigs, op = Credentials(client=mock_client).create(mock_hab, mock_registry, data, schema, recp) + + assert creder.said == "EK2xYrVkfJJHvlGhP79sfEPvQGmkFPPNAj-bjI5oHy7m" + assert iss.said == "EE8yncw1LCyBVtZPtozAFi7qvGn9dRPwTbuq--ulOAtB" + assert ixn.said == "EC5KxyucpxnOpIpHe2QUPs9YeH1yGvkALg8NcWLYFe6a" + assert op == {'v': 'ACDC10JSON00014c_'} + + verifyNoUnwantedInteractions() + unstub() + + +def test_ipex_grant(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.peer.exchanging import Exchanges + mock_excs = mock(spec=Exchanges, strict=True) + + dt = "2023-09-25T16:01:37.000000+00:00" + mock_hab = {'prefix': 'a_prefix', 'name': 'aid1', 'state': {'s': '1', 'd': "ABCDEFG"}} + mock_acdc = {} + mock_iss = {} + mock_anc = {} + mock_grant = {} + mock_gsigs = [] + mock_end = "" + expect(mock_client, times=1).exchanges().thenReturn(mock_excs) + expect(mock_excs).createExchangeMessage(sender=mock_hab, route="/ipex/grant", + payload={'m': 'this is a test', + 'i': 'ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose'}, + embeds={'acdc': {}, 'iss': {}, 'anc': {}}, dt=dt).thenReturn((mock_grant, + mock_gsigs, + mock_end)) + + ipex = credentialing.Ipex(mock_client) + recp = "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" + + grant, gsigs, end = ipex.grant(hab=mock_hab, recp=recp, message="this is a test", acdc=mock_acdc, iss=mock_iss, + anc=mock_anc, dt=dt) + + assert grant == mock_grant + assert gsigs == mock_gsigs + assert end == mock_end + + unstub() + + +def test_ipex_admit(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.peer.exchanging import Exchanges + mock_excs = mock(spec=Exchanges, strict=True) + + grant, _ = exchanging.exchange("/admit/grant", payload={}, sender="EEE") + + dt = "2023-09-25T16:01:37.000000+00:00" + mock_hab = {'prefix': 'a_prefix', 'name': 'aid1', 'state': {'s': '1', 'd': "ABCDEFG"}} + mock_admit = {} + mock_gsigs = [] + mock_end = "" + expect(mock_client, times=1).exchanges().thenReturn(mock_excs) + expect(mock_excs).createExchangeMessage(sender=mock_hab, route="/ipex/admit", + payload={'m': 'this is a test'}, + embeds=None, dt=dt, dig=grant.said).thenReturn((mock_admit, + mock_gsigs, + mock_end)) + + ipex = credentialing.Ipex(mock_client) # type: ignore + grant, gsigs, end = ipex.admit(hab=mock_hab, message="this is a test", dt=dt, grant=grant.said) + + assert grant == mock_admit + assert gsigs == mock_gsigs + assert end == mock_end + + unstub() + + +def test_submit_admit(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from requests import Response + mock_rep = mock(spec=Response, strict=True) + + expect(mock_rep).json().thenReturn(dict(b='c')) + + mock_admit = mock({'ked': dict(a='b')}) + mock_gsigs = [] + mock_end = "" + recp = ["ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose"] + + ipex = credentialing.Ipex(mock_client) # type: ignore + body = {'exn': {'a': 'b'}, 'sigs': [], 'atc': '', 'rec': ['ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose']} + expect(mock_client, times=1).post(f"/identifiers/aid1/ipex/admit", json=body).thenReturn(mock_rep) + rep = ipex.submitAdmit("aid1", exn=mock_admit, sigs=mock_gsigs, atc=mock_end, recp=recp) + + assert rep == dict(b='c') + + unstub() + + +def test_submit_grant(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from requests import Response + mock_rep = mock(spec=Response, strict=True) + + expect(mock_rep).json().thenReturn(dict(b='c')) + + mock_admit = mock({'ked': dict(a='b')}) + mock_gsigs = [] + mock_end = "" + recp = ["ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose"] + + ipex = credentialing.Ipex(mock_client) # type: ignore + body = {'exn': {'a': 'b'}, 'sigs': [], 'atc': '', 'rec': ['ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose']} + expect(mock_client, times=1).post(f"/identifiers/aid1/ipex/grant", json=body).thenReturn(mock_rep) + rep = ipex.submitGrant("aid1", exn=mock_admit, sigs=mock_gsigs, atc=mock_end, recp=recp) + + assert rep == dict(b='c') + + unstub() diff --git a/tests/app/test_ending.py b/tests/app/test_ending.py new file mode 100644 index 0000000..c6070f5 --- /dev/null +++ b/tests/app/test_ending.py @@ -0,0 +1,65 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.test_ending module + +Testing ending with unit tests +""" + +from mockito import mock, unstub, verifyNoUnwantedInteractions, expect +import pytest + +def test_end_role_authorizations_name(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.ending import EndRoleAuthorizations + ends = EndRoleAuthorizations(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock(spec=Response, strict=True) + + expect(mock_client, times=1).get('/identifiers/name/endroles/role').thenReturn(mock_response) + + expect(mock_response, times=1).json().thenReturn({'some': 'output'}) + + out = ends.list(name='name', role='role') + + assert out == {'some': 'output'} + + verifyNoUnwantedInteractions() + unstub() + +def test_end_role_authorizations_aid(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.ending import EndRoleAuthorizations + ends = EndRoleAuthorizations(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock(spec=Response, strict=True) + + expect(mock_client, times=1).get('/endroles/aid1/role').thenReturn(mock_response) + + expect(mock_response, times=1).json().thenReturn({'some': 'output'}) + + out = ends.list(aid='aid1', role='role') + + assert out == {'some': 'output'} + + verifyNoUnwantedInteractions() + unstub() + +def test_end_role_authorizations_bad(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.ending import EndRoleAuthorizations + ends = EndRoleAuthorizations(client=mock_client) # type: ignore + + with pytest.raises(ValueError, match='either `aid` or `name` is required'): + ends.list() + + verifyNoUnwantedInteractions() + unstub() diff --git a/tests/app/test_escrowing.py b/tests/app/test_escrowing.py new file mode 100644 index 0000000..8848762 --- /dev/null +++ b/tests/app/test_escrowing.py @@ -0,0 +1,29 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.test_escrowing module + +Testing escrowing with unit tests +""" + +from mockito import mock, expect, unstub, verifyNoUnwantedInteractions + +def test_end_role_authorizations_name(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.escrowing import Escrows + escrows = Escrows(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock(spec=Response, strict=True) + + expect(mock_client, times=1).get('/escrows/rpy', params={'route': '/my_route'}).thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn({'some': 'output'}) + + out = escrows.getEscrowReply(route='/my_route') + + assert out == {'some': 'output'} + + verifyNoUnwantedInteractions() + unstub() diff --git a/tests/app/test_grouping.py b/tests/app/test_grouping.py index cf24a05..b2ab0b3 100644 --- a/tests/app/test_grouping.py +++ b/tests/app/test_grouping.py @@ -1,178 +1,125 @@ -import responses -from keri.core.coring import Tiers -from signify.app.clienting import SignifyClient - -url = "http://localhost:3901" -bran = b'0123456789abcdefghijk' -tier = Tiers.low - -states = [ - {'v': 'KERI10JSON0001b6_', 'i': 'EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3', 's': '0', 'p': '', - 'd': 'EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3', 'f': '0', 'dt': '2023-03-17T20:17:22.021742+00:00', - 'et': 'icp', 'kt': '1', 'k': ['DGmIfLmgErg4zFHfPwaDckLNxsLqc5iS_P0QbLjbWR0I'], 'nt': '1', - 'n': ['EJhRr10e5p7LVB6JwLDIcgqsISktnfe5m60O_I2zZO6N'], 'bt': '0', 'b': [], 'c': [], - 'ee': {'s': '0', 'd': 'EIaGMMWJFPmtXznY1IIiKDIrg-vIyge6mBl2QV8dDjI3', 'br': [], 'ba': []}, 'di': ''}, - {'v': 'KERI10JSON0001b6_', 'i': 'EH0GZGSuPrwdqovzlEWjm3OMFCZ0GIMM8LUt70mSC3up', 's': '0', 'p': '', - 'd': 'EH0GZGSuPrwdqovzlEWjm3OMFCZ0GIMM8LUt70mSC3up', 'f': '0', 'dt': '2023-03-17T20:18:08.535887+00:00', - 'et': 'icp', 'kt': '1', 'k': ['DALBBwXiiUEgYcax8jKA6C1O7huSuoFsDJfxYfMLpaQC'], 'nt': '1', - 'n': ['ENyFbwVTyqa8prmA3KmmEvwl8KhSlcUhjWIgdXv_tcPj'], 'bt': '0', 'b': [], 'c': [], - 'ee': {'s': '0', 'd': 'EH0GZGSuPrwdqovzlEWjm3OMFCZ0GIMM8LUt70mSC3up', 'br': [], 'ba': []}, 'di': ''}, - {'v': 'KERI10JSON0001b6_', 'i': 'EHxb11g58Dam7nNDjqWD5U60v3oATS2hGbPh9WPtZhXu', 's': '0', 'p': '', - 'd': 'EHxb11g58Dam7nNDjqWD5U60v3oATS2hGbPh9WPtZhXu', 'f': '0', 'dt': '2023-03-17T20:18:28.749254+00:00', - 'et': 'icp', 'kt': '1', 'k': ['DDu97S0v2ofK2KUKOS1IqDeY7rBtppKtItAjB2nMF9gc'], 'nt': '1', - 'n': ['EJknp5MMpxG4s-IDjHKHYzc17loCUKtSsP6e1yDZrhx5'], 'bt': '0', 'b': [], 'c': [], - 'ee': {'s': '0', 'd': 'EHxb11g58Dam7nNDjqWD5U60v3oATS2hGbPh9WPtZhXu', 'br': [], 'ba': []}, 'di': ''} -] - - -def test_incept(): - client = SignifyClient(url=url, passcode=bran, tier=tier) - assert client.controller == "ELvxjlGm4zGdItzUa6Mg0ZP_gvvbisl7N5DUceKdOqGj" - - groups = client.groups() - assert groups is not None - - nstates = states - - # Test Defaults with states and nstates the same - icp = groups.incept(states, nstates) - # assert icp.pre == "EAVMqJDVOXwaO3rbca1UdHwkkzahZi8JYQDViw6fkdeE" - # assert icp.ked["t"] == "icp" - # - # assert icp.ked["kt"] == "2" - # assert icp.ked['k'] == [ - # "DGmIfLmgErg4zFHfPwaDckLNxsLqc5iS_P0QbLjbWR0I", - # "DALBBwXiiUEgYcax8jKA6C1O7huSuoFsDJfxYfMLpaQC", - # "DDu97S0v2ofK2KUKOS1IqDeY7rBtppKtItAjB2nMF9gc" - # ] - # - # assert icp.ked["nt"] == "2" - # assert icp.ked['n'] == [ - # "EJhRr10e5p7LVB6JwLDIcgqsISktnfe5m60O_I2zZO6N", - # "ENyFbwVTyqa8prmA3KmmEvwl8KhSlcUhjWIgdXv_tcPj", - # "EJknp5MMpxG4s-IDjHKHYzc17loCUKtSsP6e1yDZrhx5" - # ] - # - # assert icp.ked["bt"] == "0" - # assert icp.ked["b"] == [] - # assert icp.ked["a"] == [] - # assert icp.ked["c"] == [] - # - # # Test all other parameters - # icp = groups.incept(states, nstates, isith=["1/2", "1/2", "1/2"], nsith=["1/3", "1/3", "1/3"], toad="3", - # wits=["BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - # "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - # "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX", - # "BM35JN8XeJSEfpxopjn5jr7tAHCE5749f0OobhMLCorE"], - # estOnly=True, DnD=True, - # data=[dict(i="EImOExnAuY3_6C2J48HhGytUDAvQEB2Ypy6pLs0GxfBR", s=0, - # d="EImOExnAuY3_6C2J48HhGytUDAvQEB2Ypy6pLs0GxfBR")]) - # - # assert icp.pre == "ECQVcV-xHaJ-wyCrXnLo71aZYTidR1BElhXddadvC7m0" - # assert icp.ked["t"] == "icp" - # - # assert icp.ked["kt"] == ["1/2", "1/2", "1/2"] - # assert icp.ked['k'] == [ - # "DGmIfLmgErg4zFHfPwaDckLNxsLqc5iS_P0QbLjbWR0I", - # "DALBBwXiiUEgYcax8jKA6C1O7huSuoFsDJfxYfMLpaQC", - # "DDu97S0v2ofK2KUKOS1IqDeY7rBtppKtItAjB2nMF9gc" - # ] - # - # assert icp.ked["nt"] == ["1/3", "1/3", "1/3"] - # assert icp.ked['n'] == [ - # "EJhRr10e5p7LVB6JwLDIcgqsISktnfe5m60O_I2zZO6N", - # "ENyFbwVTyqa8prmA3KmmEvwl8KhSlcUhjWIgdXv_tcPj", - # "EJknp5MMpxG4s-IDjHKHYzc17loCUKtSsP6e1yDZrhx5" - # ] - # - # assert icp.ked["bt"] == "3" - # assert icp.ked["b"] == ["BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", - # "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - # "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX", - # "BM35JN8XeJSEfpxopjn5jr7tAHCE5749f0OobhMLCorE"] - # assert icp.ked["a"] == [ - # { - # "i": "EImOExnAuY3_6C2J48HhGytUDAvQEB2Ypy6pLs0GxfBR", - # "s": 0, - # "d": "EImOExnAuY3_6C2J48HhGytUDAvQEB2Ypy6pLs0GxfBR" - # } - # ] - # assert icp.ked["c"] == ["EO", "DND"] - # - # # Test delegation - # icp = groups.incept(states, nstates, delpre="EImOExnAuY3_6C2J48HhGytUDAvQEB2Ypy6pLs0GxfBR") - # assert icp.pre == "EG3BYTUJQ76D8mQdISRx76OTn3FWhIJgitFF9wJ6JTZr" - # assert icp.ked["t"] == "dip" - # assert icp.ked["di"] == "EImOExnAuY3_6C2J48HhGytUDAvQEB2Ypy6pLs0GxfBR" - - -@responses.activate -def test_group_recipe(): - client = SignifyClient(url=url, passcode=bran, tier=tier) - assert client.controller == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - - rsp1 = responses.Response( - method="GET", - url="http://localhost:3901/boot", - json={"kel": [{"ked": {"v": "KERI10JSON000159_", "t": "icp", - "d": "EIDJUg2eR8YGZssffpuqQyiXcRVz2_Gw_fcAVWpUMie1", - "i": "EIDJUg2eR8YGZssffpuqQyiXcRVz2_Gw_fcAVWpUMie1", "s": "0", "kt": "1", - "k": ["DF_pwiZQiNWtvksHhOtoAYM6j3WTdzBmMQainmlbxSdT"], "nt": "1", - "n": ["EAksAMJTf2Cd1JYUq7fWy22hdPUFijBgrej4Wtfx2NFM"], "bt": "0", "b": [], "c": [], - "a": ["ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose"]}, - "sig": - "AAA8XCnIKkWfpgayz6GRfVPH_Fe4P55XF75fzY3iLT5YQsBiofNBWJI9OkiYro98b3Qrh85DDTW52cZ5fj2yz3gF"}], - "pidx": 0} +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.test_grouping module + +Testing grouping with unit tests +""" + +import pytest +from mockito import mock, verify, verifyNoUnwantedInteractions, unstub, expect + + +def test_grouping_get_request(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.grouping import Groups + groups = Groups(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock({'headers': {'content-range': 'aids 0-10/2'}}, spec=Response, strict=True) + expect(mock_client, times=1).get('/multisig/request/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4').thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn( + [{'d': "EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4"}] + ) + + res = groups.get_request(said="EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4") + assert len(res) == 1 + assert res[0]['d'] == "EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4" + + verifyNoUnwantedInteractions() + unstub() + + +def test_grouping_send_request(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.grouping import Groups + groups = Groups(client=mock_client) # type: ignore + + from requests import Response + + mock_exn = {} + mock_sigs = ['sig'] + mock_atc = b'-attachment' + + body = { + 'exn': mock_exn, + 'sigs': mock_sigs, + 'atc': mock_atc.decode("utf-8") + } + + mock_response = mock({'headers': {'content-range': 'aids 0-10/2'}}, spec=Response, strict=True) + expect(mock_client, times=1).post( + '/identifiers/test/multisig/request', + json=body).thenReturn(mock_response) + + expect(mock_response, times=1).json().thenReturn( + {'t': 'exn', 'd': "EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4"} ) - responses.add(rsp1) - rsp2 = responses.Response( - method="POST", - url="http://localhost:3901/boot", - status=202 + + res = groups.send_request(name="test", exn=mock_exn, sigs=mock_sigs, atc=mock_atc) + assert res['t'] == 'exn' + assert res['d'] == "EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4" + + verifyNoUnwantedInteractions() + unstub() + + +def test_grouping_join(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.core import keeping + mock_manager = mock(spec=keeping.Manager, strict=True) + mock_client.manager = mock_manager # type: ignore + + from signify.app.grouping import Groups + groups = Groups(client=mock_client) # type: ignore + + from requests import Response + from keri.core.serdering import SerderKERI + mock_rot = mock({'ked': {}}, spec=SerderKERI, strict=True) + mock_sigs = ['sig'] + mock_smids = ['1', '2', '3'] + mock_rmids = ['a', 'b', 'c'] + + body = { + 'tpc': 'multisig', + 'rot': {}, + 'sigs': mock_sigs, + 'gid': "EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4", + 'smids': mock_smids, + 'rmids': mock_rmids + } + + mock_response = mock({'headers': {'content-range': 'aids 0-10/2'}}, spec=Response, strict=True) + expect(mock_client, times=1).post( + '/identifiers/test/multisig/join', + json=body).thenReturn(mock_response) + + expect(mock_response, times=1).json().thenReturn( + {'t': 'op', 'd': "EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4"} ) - responses.add(rsp2) - - client.connect() - assert client.agent is not None - assert client.agent.delpre == "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" - assert client.agent.pre == "EIDJUg2eR8YGZssffpuqQyiXcRVz2_Gw_fcAVWpUMie1" - assert client.ctrl.ridx == 0 - - groups = client.groups() - assert groups is not None - keyStates = client.keyStates() - assert keyStates is not None - - responses.add(responses.Response( - method="GET", - url="http://localhost:3901/states?pre=EIDJUg2eR8YGZssffpuqQyiXcRVz2_Gw_fcAVWpUMie1&" - "pre=EG3BYTUJQ76D8mQdISRx76OTn3FWhIJgitFF9wJ6JTZr&pre=ECQVcV-xHaJ-wyCrXnLo71aZYTidR1BElhXddadvC7m0", - json=states - )) - - sts = keyStates.list(pres=["EIDJUg2eR8YGZssffpuqQyiXcRVz2_Gw_fcAVWpUMie1", - "EG3BYTUJQ76D8mQdISRx76OTn3FWhIJgitFF9wJ6JTZr", - "ECQVcV-xHaJ-wyCrXnLo71aZYTidR1BElhXddadvC7m0"]) - assert len(states) == 3 - - nstates = states - # Test all other parameters - # icp = groups.incept(sts, nstates, isith=["1/2", "1/2", "1/2"], nsith=["1/3", "1/3", "1/3"]) - # - # assert icp.pre == "EIKVdH89EFGZghyxZVNf-WxE6EpANuPLMTTBVYbUxbBG" - # assert icp.ked["t"] == "icp" - # - # assert icp.ked["kt"] == ["1/2", "1/2", "1/2"] - # assert icp.ked['k'] == [ - # "DGmIfLmgErg4zFHfPwaDckLNxsLqc5iS_P0QbLjbWR0I", - # "DALBBwXiiUEgYcax8jKA6C1O7huSuoFsDJfxYfMLpaQC", - # "DDu97S0v2ofK2KUKOS1IqDeY7rBtppKtItAjB2nMF9gc" - # ] - # - # assert icp.ked["nt"] == ["1/3", "1/3", "1/3"] - # assert icp.ked['n'] == [ - # "EJhRr10e5p7LVB6JwLDIcgqsISktnfe5m60O_I2zZO6N", - # "ENyFbwVTyqa8prmA3KmmEvwl8KhSlcUhjWIgdXv_tcPj", - # "EJknp5MMpxG4s-IDjHKHYzc17loCUKtSsP6e1yDZrhx5" - # ] + + res = groups.join(name="test", rot=mock_rot, sigs=mock_sigs, gid="EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4", + smids=mock_smids, rmids=mock_rmids) + assert res['t'] == 'op' + assert res['d'] == "EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4" + + verifyNoUnwantedInteractions() + unstub() + + + diff --git a/tests/app/test_notifying.py b/tests/app/test_notifying.py new file mode 100644 index 0000000..975c70f --- /dev/null +++ b/tests/app/test_notifying.py @@ -0,0 +1,81 @@ +# -*- encoding: utf-8 -*- +""" +SIGNIFY +signify.app.test_notifying module + +Testing notifying with unit tests +""" + +from mockito import mock, unstub, expect, verifyNoUnwantedInteractions + + +def test_notification_list(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient) + + from signify.app.notifying import Notifications + notes = Notifications(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock({'headers': {'content-range': 'notes 0-10/20'}}, spec=Response, strict=True) + expect(mock_client, times=1).get('/notifications', headers=dict(Range=f"notes={0}-{24}")).thenReturn(mock_response) + expect(mock_response, times=1).json().thenReturn( + ['note1', 'note2'] + ) + + out = notes.list() + assert out['start'] == 0 + assert out['end'] == 10 + assert out['total'] == 20 + assert out['notes'] == ['note1', 'note2'] + + verifyNoUnwantedInteractions() + unstub() + + +def test_noticiation_mark_as_read(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.notifying import Notifications + notes = Notifications(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock({'status_code': 202}, spec=Response, strict=True) + expect(mock_client, times=1).put('/notifications/ABC123', json={}).thenReturn(mock_response) + + out = notes.markAsRead(nid="ABC123") + assert out is True + + mock_response = mock({'status_code': 404}, spec=Response, strict=True) + expect(mock_client, times=1).put('/notifications/DEF456', json={}).thenReturn(mock_response) + + out = notes.markAsRead(nid="DEF456") + assert out is False + + verifyNoUnwantedInteractions() + unstub() + + +def test_noticiation_delete(): + from signify.app.clienting import SignifyClient + mock_client = mock(spec=SignifyClient, strict=True) + + from signify.app.notifying import Notifications + notes = Notifications(client=mock_client) # type: ignore + + from requests import Response + mock_response = mock({'status_code': 202}, spec=Response, strict=True) + expect(mock_client, times=1).delete(path='/notifications/ABC123').thenReturn(mock_response) + + out = notes.delete(nid="ABC123") + assert out is True + + mock_response = mock({'status_code': 404}, spec=Response, strict=True) + expect(mock_client, times=1).delete(path='/notifications/DEF456').thenReturn(mock_response) + + out = notes.delete(nid="DEF456") + assert out is False + + verifyNoUnwantedInteractions() + unstub() diff --git a/tests/app/witness.toml b/tests/app/witness.toml deleted file mode 100755 index 9ea43a9..0000000 --- a/tests/app/witness.toml +++ /dev/null @@ -1,94 +0,0 @@ -responses: -- response: - auto_calculate_content_length: false - body: '' - content_type: text/plain - method: POST - status: 202 - url: http://localhost:3903/boot -- response: - auto_calculate_content_length: false - body: '{"agent": {"v": "KERI10JSON0001e2_", "i": "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei", - "s": "0", "p": "", "d": "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei", "f": - "0", "dt": "2023-05-19T17:25:10.486545+00:00", "et": "dip", "kt": "1", "k": - ["DMZh_y-H5C3cSbZZST-fqnsmdNTReZxIh0t2xSTOJQ8a"], "nt": "1", "n": ["EM9M2EQNCBK0MyAhVYBvR98Q0tefpvHgE-lHLs82XgqC"], - "bt": "0", "b": [], "c": [], "ee": {"s": "0", "d": "EEXekkGu9IAzav6pZVJhkLnjtjM5v3AcyA-pdKUcaGei", - "br": [], "ba": []}, "di": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose"}, - "controller": {"state": {"v": "KERI10JSON0001b6_", "i": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", - "s": "0", "p": "", "d": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", "f": - "0", "dt": "2023-05-19T17:25:10.507921+00:00", "et": "icp", "kt": "1", "k": - ["DAbWjobbaLqRB94KiAutAHb_qzPpOHm3LURA_ksxetVc"], "nt": "1", "n": ["EIFG_uqfr1yN560LoHYHfvPAhxQ5sN6xZZT_E3h7d2tL"], - "bt": "0", "b": [], "c": [], "ee": {"s": "0", "d": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", - "br": [], "ba": []}, "di": ""}, "ee": {"v": "KERI10JSON00012b_", "t": "icp", - "d": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", "i": "ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose", - "s": "0", "kt": "1", "k": ["DAbWjobbaLqRB94KiAutAHb_qzPpOHm3LURA_ksxetVc"], - "nt": "1", "n": ["EIFG_uqfr1yN560LoHYHfvPAhxQ5sN6xZZT_E3h7d2tL"], "bt": "0", - "b": [], "c": [], "a": []}}, "pidx": 0}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/agent/ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose -- response: - auto_calculate_content_length: false - body: '' - content_type: text/plain - method: PUT - status: 204 - url: http://localhost:3901/agent/ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose?type=ixn -- response: - auto_calculate_content_length: false - body: '{"name": "witness.EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", "metadata": - {"sn": 0}, "done": false, "error": null, "response": null}' - content_type: text/plain - method: POST - status: 202 - url: http://localhost:3901/identifiers -- response: - auto_calculate_content_length: false - body: '{"name": "witness.EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", "metadata": - {"sn": 0}, "done": false, "error": null, "response": null}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/operations/witness.EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa -- response: - auto_calculate_content_length: false - body: '{"name": "witness.EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", "metadata": - {"sn": 0}, "done": true, "error": null, "response": {"v": "KERI10JSON0001b7_", - "t": "icp", "d": "EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", "i": "EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", - "s": "0", "kt": "1", "k": ["DIi2YyvzYvQBveyQ-a3_0kbeBLIomgVd9mB0MEwmnuah"], - "nt": "1", "n": ["EFirR01VeAFjvgr5VBLBkpkl22hsEIfk6boz3vgO42KJ"], "bt": "2", - "b": ["BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"], "c": [], "a": []}}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/operations/witness.EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa -- response: - auto_calculate_content_length: false - body: '{"name": "aid1", "prefix": "EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", - "salty": {"sxlt": "1AAHoWyngs6QoxnxPn29gYZMjXwIvbDORlLBKmD82fEdewgNEeiD0RsXhcRLztSo7-0fPo-s39q4H2SYfEzn7V0vbDw6c_fR3sMJ", - "pidx": 0, "kidx": 0, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A"], "ncodes": ["A"], "transferable": true}, "transferable": true, "state": - {"v": "KERI10JSON000242_", "i": "EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", - "s": "0", "p": "", "d": "EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", "f": - "0", "dt": "2023-05-19T17:25:11.098106+00:00", "et": "icp", "kt": "1", "k": - ["DIi2YyvzYvQBveyQ-a3_0kbeBLIomgVd9mB0MEwmnuah"], "nt": "1", "n": ["EFirR01VeAFjvgr5VBLBkpkl22hsEIfk6boz3vgO42KJ"], - "bt": "2", "b": ["BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha", "BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM", - "BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"], "c": [], "ee": {"s": "0", "d": - "EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", "br": [], "ba": []}, "di": ""}, - "windexes": [0, 1, 2]}' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/identifiers/aid1 -- response: - auto_calculate_content_length: false - body: '[{"name": "aid1", "prefix": "EGTFIbnFoA7G-f4FHzzXUMp6VAgQfJ-2nXqzfb5hVwKa", - "salty": {"sxlt": "1AAHoWyngs6QoxnxPn29gYZMjXwIvbDORlLBKmD82fEdewgNEeiD0RsXhcRLztSo7-0fPo-s39q4H2SYfEzn7V0vbDw6c_fR3sMJ", - "pidx": 0, "kidx": 0, "stem": "signify:aid", "tier": "low", "dcode": "E", "icodes": - ["A"], "ncodes": ["A"], "transferable": true}}]' - content_type: text/plain - method: GET - status: 200 - url: http://localhost:3901/identifiers?last=&limit=25 diff --git a/tests/conftest.py b/tests/conftest.py index 7f5be74..be2ec01 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,16 +9,6 @@ from keri.core import coring from keri.help import helping -from keria.testing import testing_helper - -@pytest.fixture -def helpers(): - return testing_helper.Helpers - -@pytest.fixture -def seeder(): - return testing_helper.DbSeed - @pytest.fixture() def mockHelpingNowUTC(monkeypatch): """ diff --git a/tests/core/test_authing.py b/tests/core/test_authing.py index b52059b..b6b5574 100644 --- a/tests/core/test_authing.py +++ b/tests/core/test_authing.py @@ -1,132 +1,309 @@ # -*- encoding: utf-8 -*- """ SIGNIFY -signify.core.authing module +signify.app.test_authing module -Testing authentication +Testing authing with unit tests """ -import pytest -from falcon import testing + from keri import kering -from keri.app import habbing -from keri.core import parsing, eventing, coring +from keri.core import serdering from keri.core.coring import Tiers -from keri.db import dbing -from keri.end import ending -from signify.core.authing import Authenticater -from keria.testing.testing_helper import Helpers -from signify.core import authing - -agentPre="EH1arTrTyQkrxK-cog7rzjahB0skymgrDsPbPcg45sC9" -userPub="ELI7pg979AdhmvrjDeam2eAO2SR5niCgnjAJXJHtJose" -userAid="EK35JRNdfVkO4JwhXaSTdV4qzB_ibk_tGJmSVcY4pZqx" - -def test_authenticater(mockHelpingNowUTC): - bran = b'0123456789abcdefghijk' - ctrl = authing.Controller(bran=bran, tier=Tiers.low) - with Helpers.openKeria(salter=ctrl.salter) as (agency, agent, app, client): - - dgkey = dbing.dgKey(agent.agentHab.pre, agent.agentHab.kever.serder.said) # get message - raw = agent.hby.db.getEvt(key=dgkey) - serder = coring.Serder(raw=bytes(raw)) - - sigs = agent.hby.db.getSigs(key=dgkey) - evt = dict( - ked=serder.ked, - sig=coring.Siger(qb64b=bytes(sigs[0])).qb64 - ) - - # signify agent - sAgent = authing.Agent(state=evt["ked"]) - - # Create authenticater with Agent and controllers AID - authn = authing.Authenticater(agent=sAgent, ctrl=ctrl) - - method = "POST" - path = "/boot" - headers = dict([ - ("Content-Type", "application/json"), - ("content-length", "256"), - ("Connection", "close"), - ("signify-resource", ctrl.pre), - ("signify-timestamp", "2022-09-24T00:05:48.196795+00:00"), - ]) - - header, qsig = ending.siginput("signify", method, path, headers, fields=Authenticater.DefaultFields, hab=agent.agentHab, - alg="ed25519", keyid=agent.pre) - headers |= header - signage = ending.Signage(markers=dict(signify=qsig), indexed=False, signer=None, ordinal=None, digest=None, - kind=None) - headers |= ending.signature([signage]) - - headd = dict(headers) - assert headd['Connection'] == 'close' - assert headd['Content-Type'] == 'application/json' - assert headd['Signature'] == 'indexed="?0";signify="0BAuFfKJ-Kl7zfH5aWXDz9F0njST3t9NH4icNKpiF_NP0BnUqWx0YVIjdfQlXz-7BM2YtDJCZO5Jr4LuDyPDaucD"' - assert headd['Signature-Input'] == f'signify=("@method" "@path" "content-length" "signify-resource" "signify-timestamp");created=1609459200;keyid="{agentPre}";alg="ed25519"' - assert headd['content-length'] == '256' - assert headd['signify-resource'] == ctrl.pre - assert headd['signify-timestamp'] == '2022-09-24T00:05:48.196795+00:00' - req = testing.create_req(method="POST", path="/boot", headers=dict(headers)) - assert authn.verifysig(req.headers, "POST", "/boot") - - -def test_agent(): - bran = b'0123456789abcdefghijk' - ctrl = authing.Controller(bran=bran, tier=Tiers.low) - with Helpers.openKeria(salter=ctrl.salter) as (agency, kAgent, app, client): - kel = [] - ahab = kAgent.agentHab - assert ahab.pre == f"{agentPre}" - assert ahab.kever.verfers[0].qb64 == "DCajWNxkIK7FQWTDZpcvv3_EcRDj6HWVVx-HFEjrmBPL" - icp, sigs, _ = ahab.getOwnEvent(sn=0) - kel.append(dict(ked=icp.ked, sig=sigs[0].qb64)) - sAgent = authing.Agent(state=icp.ked) - - ahab.rotate() - rot, sigs, _ = ahab.getOwnEvent(sn=1) - kel.append(dict(ked=rot.ked, sig=sigs[0].qb64)) - assert rot.said == "EAZDlwT9z6269nmW6yjuIOYIR-GR6soqqdYvIS8FUd_m" - assert ahab.kever.verfers[0].qb64 == "DB9RaU1cm3PcpJWcSZ_w_qFmap3fr5qgVXhQy5yXJLAo" - - ahab.rotate() - rot, sigs, _ = ahab.getOwnEvent(sn=2) - kel.append(dict(ked=rot.ked, sig=sigs[0].qb64)) - assert rot.said == "EI3uC6o-o9h62hjEz6i2lEWQUIPDI4GCDfF7Gw2eXPRv" - - assert ahab.kever.sn == 2 - - assert kAgent.pre == ahab.pre - assert sAgent.pre == ahab.pre - assert kAgent.caid == userAid - assert sAgent.delpre == userAid - assert sAgent.verfer.qb64 == 'DCajWNxkIK7FQWTDZpcvv3_EcRDj6HWVVx-HFEjrmBPL' - assert ahab.kever.verfers[0].qb64 == "DFzRzCjvWRuMfcCo2CiOpx-sWXEHxDByv2J907Yqb-Nq" - # TODO: This used to be an equality check, but rotation should make it non-equal? - assert sAgent.verfer.qb64 != ahab.kever.verfers[0].qb64 - - # Inception event with 2 keys is invalid - orig=icp.ked["k"] - # adding agent verfer is non-sensicle but helpful for causing the exception - badTestValue=[icp.verfers[0].qb64,coring.Verfer(qb64=ahab.kever.verfers[0].qb64).qb64] - icp.ked["k"]=badTestValue - with pytest.raises(kering.ValidationError) as ex: - _ = authing.Agent(state=icp.ked) - - assert ex.value.args[0] == "agent inception event can only have one key" - - # reset to original value - icp.ked["k"]=orig - - # TODO we used to validate the next key of the agent, but our Agent doesn't validate next keys - # Inception event with 2 next keys is invalid - # orig=icp.ked["n"] - # # adding agent diger is non-sensicle but helpful for causing the exception - # badTestValue=[icp.digers[0].qb64,coring.Diger(qb64=ahab.kever.digers[0].qb64).qb64] - # icp.ked["n"]=badTestValue - - # with pytest.raises(kering.ValidationError) as ex: - # _ = authing.Agent(state=icp.ked) - - # assert ex.value.args[0] == "agent inception event can only have one next key" +from mockito import mock, unstub, expect, verifyNoUnwantedInteractions +import pytest + + +def rt(a, b, c): + return True + + +def test_verify(): + agent = mock({'pre': "EEz01234"}) + ctrl = mock() + + from signify.core.authing import Authenticater + authn = Authenticater(agent, ctrl) + + import requests + mock_request = mock({'method': 'GET', 'url': 'http://example.com/my_path', 'headers': {}, 'body': "a body for len"}, + spec=requests.Request, strict=True) + mock_rep = mock({'request': mock_request, 'headers': {}}, spec=requests.Response, strict=True) + + with pytest.raises(kering.AuthNError): + authn.verify(rep=mock_rep) + + mock_rep = mock({'request': mock_request, 'headers': {"SIGNIFY-RESOURCE": 'EABC'}}, spec=requests.Response, + strict=True) + + with pytest.raises(kering.AuthNError): + authn.verify(rep=mock_rep) + + # Swap out verifysig so we can test verify + authn.verifysig = rt + mock_rep = mock({'request': mock_request, 'headers': {"SIGNIFY-RESOURCE": 'EEz01234'}}, spec=requests.Response, + strict=True) + + authn.verify(rep=mock_rep) + verifyNoUnwantedInteractions() + unstub() + + +def test_agent(): + mock_verfer = mock() + from keri.core import coring + expect(coring, times=1).Verfer(qb64="key").thenReturn(mock_verfer) + + keys = ['key'] + state = { + 'i': 'pre', + 's': 0, + 'di': 'delpre', + 'd': 'said', + 'k': keys, + } + from signify.core.authing import Agent + agent = Agent(state=state) + + assert agent.pre == "pre" + assert agent.delpre == "delpre" + assert agent.said == "said" + assert agent.sn == 0 + assert agent.verfer == mock_verfer + + verifyNoUnwantedInteractions() + unstub() + + from keri import kering + with pytest.raises(kering.ValidationError): + keys.append("another key") + state['k'] = keys + agent = Agent(state=state) + + +@pytest.mark.parametrize('bran', [ + ("abcdefghijklmnop01234"), + (b"abcdefghijklmnop01234"), +]) +def test_controller(bran): + from signify.core.authing import Controller + from keri.core.coring import Tiers + ctrl = Controller(bran=bran, tier=Tiers.low) + + assert ctrl.bran == "0AAabcdefghijklmnop01234" + assert ctrl.stem == "signify:controller" + + assert ctrl.tier == Tiers.low + + from keri.core import coring + assert type(ctrl.salter) is coring.Salter + assert type(ctrl.signer) is coring.Signer + assert ctrl.signer.code == "A" + assert ctrl.signer.qb64 == "AF1iHYsl-7DZFD71kcsg5iUAkLP3Lh_01RZFEHhL3629" + + assert type(ctrl.nsigner) is coring.Signer + assert ctrl.nsigner.code == "A" + assert ctrl.nsigner.qb64 == "AGG0prnUWeKJGfh00-rrSqBIxR0Mx5K1FP0XC_UtCdjX" + + assert ctrl.keys == ["DEps8kAE90Ab9Fs_MLaES9Pre-ba3eOZCY2H7HIENVug"] + assert ctrl.ndigs == ["EAioAm-C0hG3oG4NplWhh7Uc43C2cpkbLX2Bj5yIKkna"] + + raw = b'{"v":"KERI10JSON00012b_","t":"icp","d":"EMPYj-h2OoCyPGQoUUd1tLUYe62YD_8A3jjXxqYawLcV","i":"EMPYj-h2OoCyPGQoUUd1tLUYe62YD_8A3jjXxqYawLcV","s":"0","kt":"1","k":["DEps8kAE90Ab9Fs_MLaES9Pre-ba3eOZCY2H7HIENVug"],"nt":"1","n":["EAioAm-C0hG3oG4NplWhh7Uc43C2cpkbLX2Bj5yIKkna"],"bt":"0","b":[],"c":[],"a":[]}' + assert ctrl.serder.raw == raw + assert ctrl.pre == "EMPYj-h2OoCyPGQoUUd1tLUYe62YD_8A3jjXxqYawLcV" + + # self serder + assert ctrl.event()[0].raw == raw + # self.signer.sign + assert ctrl.event()[1].raw == (b'\x8a\xf6\x7f\x9e\xc8%\xc4\xe9\xc1