Skip to content

Commit

Permalink
Add upgrade to anoncreds via api
Browse files Browse the repository at this point in the history
Signed-off-by: jamshale <jamiehalebc@gmail.com>
  • Loading branch information
jamshale committed Mar 21, 2024
1 parent 6a432ea commit 6a3c56b
Show file tree
Hide file tree
Showing 23 changed files with 1,228 additions and 53 deletions.
21 changes: 20 additions & 1 deletion aries_cloudagent/admin/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
setup_aiohttp_apispec,
validation_middleware,
)

from marshmallow import fields

from ..config.injection_context import InjectionContext
Expand All @@ -38,6 +37,7 @@
from ..utils.stats import Collector
from ..utils.task_queue import TaskQueue
from ..version import __version__
from ..wallet.upgrade_singleton import UpgradeSingleton
from .base_server import BaseAdminServer
from .error import AdminSetupError
from .request_context import AdminRequestContext
Expand All @@ -58,6 +58,8 @@
"acapy::keylist::updated": "keylist",
}

upgrade_singleton = UpgradeSingleton()


class AdminModulesSchema(OpenAPISchema):
"""Schema for the modules endpoint."""
Expand Down Expand Up @@ -205,6 +207,17 @@ async def ready_middleware(request: web.BaseRequest, handler: Coroutine):
raise web.HTTPServiceUnavailable(reason="Shutdown in progress")


@web.middleware
async def upgrade_middleware(request: web.BaseRequest, handler: Coroutine):
"""Blocking middleware for upgrades."""
context: AdminRequestContext = request["context"]

if context._profile.name in upgrade_singleton.current_upgrades:
raise web.HTTPServiceUnavailable(reason="Upgrade in progress")

return await handler(request)


@web.middleware
async def debug_middleware(request: web.BaseRequest, handler: Coroutine):
"""Show request detail in debug log."""
Expand Down Expand Up @@ -351,6 +364,8 @@ async def check_multitenant_authorization(request: web.Request, handler):

is_multitenancy_path = path.startswith("/multitenancy")
is_server_path = path in self.server_paths or path == "/features"
# allow base wallets to trigger update through api
is_upgrade_path = path.startswith("/anoncreds/wallet/upgrade")

# subwallets are not allowed to access multitenancy routes
if authorization_header and is_multitenancy_path:
Expand Down Expand Up @@ -380,6 +395,7 @@ async def check_multitenant_authorization(request: web.Request, handler):
and not is_unprotected_path(path)
and not base_limited_access_path
and not (request.method == "OPTIONS") # CORS fix
and not is_upgrade_path
):
raise web.HTTPUnauthorized()

Expand Down Expand Up @@ -453,6 +469,9 @@ async def setup_context(request: web.Request, handler):

middlewares.append(setup_context)

# Upgrade middleware needs the context setup
middlewares.append(upgrade_middleware)

# Register validation_middleware last avoiding unauthorized validations
middlewares.append(validation_middleware)

Expand Down
35 changes: 31 additions & 4 deletions aries_cloudagent/admin/tests/test_admin_server.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import gc
import json
from unittest import IsolatedAsyncioTestCase

import pytest
from aries_cloudagent.tests import mock
from unittest import IsolatedAsyncioTestCase
from aiohttp import ClientSession, DummyCookieJar, TCPConnector, web
from aiohttp.test_utils import unused_port

from aries_cloudagent.tests import mock

from ...config.default_context import DefaultContextBuilder
from ...config.injection_context import InjectionContext
from ...core.event_bus import Event
from ...core.goal_code_registry import GoalCodeRegistry
from ...core.in_memory import InMemoryProfile
from ...core.protocol_registry import ProtocolRegistry
from ...core.goal_code_registry import GoalCodeRegistry
from ...utils.stats import Collector
from ...utils.task_queue import TaskQueue

from ...wallet.upgrade_singleton import UpgradeSingleton
from .. import server as test_module
from ..request_context import AdminRequestContext
from ..server import AdminServer, AdminSetupError


Expand Down Expand Up @@ -477,6 +479,31 @@ async def test_server_health_state(self):
assert response.status == 503
await server.stop()

async def test_upgrade_middleware(self):
upgrade_singleton = UpgradeSingleton()
self.context = AdminRequestContext.test_context(
{}, InMemoryProfile.test_profile()
)
self.request_dict = {
"context": self.context,
}
request = mock.MagicMock(
method="GET",
path_qs="/schemas/created",
match_info={},
__getitem__=lambda _, k: self.request_dict[k],
)
handler = mock.CoroutineMock()

await test_module.upgrade_middleware(request, handler)

upgrade_singleton.set_wallet("test-profile")
with self.assertRaises(test_module.web.HTTPServiceUnavailable):
await test_module.upgrade_middleware(request, handler)

upgrade_singleton.remove_wallet("test-profile")
await test_module.upgrade_middleware(request, handler)


@pytest.fixture
async def server():
Expand Down
6 changes: 1 addition & 5 deletions aries_cloudagent/anoncreds/default/legacy_indy/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -1123,18 +1123,14 @@ async def fix_ledger_entry(

async def txn_submit(
self,
profile: Profile,
ledger: BaseLedger,
ledger_transaction: str,
sign: bool = None,
taa_accept: bool = None,
sign_did: DIDInfo = sentinel,
write_ledger: bool = True,
) -> str:
"""Submit a transaction to the ledger."""
ledger = profile.inject(BaseLedger)

if not ledger:
raise LedgerError("No ledger available")

try:
async with ledger:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from base58 import alphabet

from .....anoncreds.base import (
AnonCredsRegistrationError,
AnonCredsSchemaAlreadyExists,
)
from .....anoncreds.models.anoncreds_schema import (
Expand All @@ -21,7 +20,7 @@
from .....connections.models.conn_record import ConnRecord
from .....core.in_memory.profile import InMemoryProfile
from .....ledger.base import BaseLedger
from .....ledger.error import LedgerError, LedgerObjectAlreadyExistsError
from .....ledger.error import LedgerObjectAlreadyExistsError
from .....messaging.responder import BaseResponder
from .....protocols.endorse_transaction.v1_0.manager import (
TransactionManager,
Expand Down Expand Up @@ -728,27 +727,16 @@ async def test_register_revocation_registry_definition_with_create_transaction_a
assert mock_create_record.called

async def test_txn_submit(self):
self.profile.inject = mock.MagicMock(
side_effect=[
None,
mock.CoroutineMock(
txn_submit=mock.CoroutineMock(side_effect=LedgerError("test error"))
),
mock.CoroutineMock(
txn_submit=mock.CoroutineMock(return_value="transaction response")
),
]
self.profile.context.injector.bind_instance(
BaseLedger,
mock.MagicMock(
txn_submit=mock.CoroutineMock(return_value="transaction_id")
),
)

# No ledger
with self.assertRaises(LedgerError):
await self.registry.txn_submit(self.profile, "test_txn")
# Write error
with self.assertRaises(AnonCredsRegistrationError):
await self.registry.txn_submit(self.profile, "test_txn")

result = await self.registry.txn_submit(self.profile, "test_txn")
assert result == "transaction response"
async with self.profile.session() as session:
ledger = session.inject(BaseLedger)
result = await self.registry.txn_submit(ledger, "test_txn")
assert result == "transaction_id"

async def test_register_revocation_list_no_endorsement(self):
self.profile.context.injector.bind_instance(
Expand Down
28 changes: 27 additions & 1 deletion aries_cloudagent/core/conductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""

import asyncio
import hashlib
import json
import logging
Expand Down Expand Up @@ -40,7 +41,9 @@
BaseMultipleLedgerManager,
MultipleLedgerManagerError,
)
from ..ledger.multiple_ledger.ledger_requests_executor import IndyLedgerRequestsExecutor
from ..ledger.multiple_ledger.ledger_requests_executor import (
IndyLedgerRequestsExecutor,
)
from ..ledger.multiple_ledger.manager_provider import MultiIndyLedgerManagerProvider
from ..messaging.responder import BaseResponder
from ..multitenant.base import BaseMultitenantManager
Expand Down Expand Up @@ -71,10 +74,14 @@
from ..transport.outbound.message import OutboundMessage
from ..transport.outbound.status import OutboundSendStatus
from ..transport.wire_format import BaseWireFormat
from ..utils.profiles import get_subwallet_profiles_from_storage
from ..utils.stats import Collector
from ..utils.task_queue import CompletedTask, TaskQueue
from ..vc.ld_proofs.document_loader import DocumentLoader
from ..version import RECORD_TYPE_ACAPY_VERSION, __version__
from ..wallet.anoncreds_upgrade import (
upgrade_wallet_to_anoncreds,
)
from ..wallet.did_info import DIDInfo
from .dispatcher import Dispatcher
from .error import StartupError
Expand Down Expand Up @@ -522,6 +529,8 @@ async def start(self) -> None:
except Exception:
LOGGER.exception("Error accepting mediation invitation")

await self.check_for_wallet_upgrades_in_progress()

# notify protcols of startup status
await self.root_profile.notify(STARTUP_EVENT_TOPIC, {})

Expand Down Expand Up @@ -823,3 +832,20 @@ async def check_for_valid_wallet_type(self, profile):
raise StartupError(
f"Wallet type config [{storage_type_from_config}] doesn't match with the wallet type in storage [{storage_type_record.value}]" # noqa: E501
)

async def check_for_wallet_upgrades_in_progress(self):
"""Check for upgrade and upgrade if needed."""
multitenant_mgr = self.context.inject_or(BaseMultitenantManager)
if multitenant_mgr:
subwallet_profiles = await get_subwallet_profiles_from_storage(
self.root_profile
)
await asyncio.gather(
*[
upgrade_wallet_to_anoncreds(profile, True)
for profile in subwallet_profiles
]
)

else:
await upgrade_wallet_to_anoncreds(self.root_profile)
Loading

0 comments on commit 6a3c56b

Please sign in to comment.