Skip to content

Commit

Permalink
Merge pull request #1330 from bcgov/block-api-for-deleted-tenant
Browse files Browse the repository at this point in the history
Improvements to Tenant Deletion
  • Loading branch information
Gavinok authored and loneil committed Oct 26, 2024
1 parent 9291ae4 commit c39fceb
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 16 deletions.
6 changes: 3 additions & 3 deletions charts/traction/templates/acapy/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ spec:
--endpoint https://{{ include "acapy.host" . }} \
--arg-file '/home/aries/argfile.yml' \
--plugin 'aries_cloudagent.messaging.jsonld' \
{{- if .Values.acapy.plugins.multitenantProvider }}
--plugin multitenant_provider.v1_0 \
{{- end }}
{{- if .Values.acapy.plugins.tractionInnkeeper }}
--plugin traction_plugins.traction_innkeeper.v1_0 \
--plugin-config-value traction_innkeeper.innkeeper_wallet.tenant_id=\"$(INNKEEPER_WALLET_TENANT_ID)\" \
Expand All @@ -62,9 +65,6 @@ spec:
{{- if .Values.acapy.plugins.connectionUpdate }}
--plugin connection_update.v1_0 \
{{- end }}
{{- if .Values.acapy.plugins.multitenantProvider }}
--plugin multitenant_provider.v1_0 \
{{- end }}
{{- if .Values.acapy.plugins.rpc }}
--plugin rpc.v1_0 \
{{- end }}
Expand Down
40 changes: 39 additions & 1 deletion plugins/traction_innkeeper/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions plugins/traction_innkeeper/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ bcrypt = "^4.2.0"
mergedeep = "^1.3.4"
typing-extensions = "4.8.0"
anoncreds = "^0.2.0"
multitenant-provider = {git = "https://github.com/hyperledger/aries-acapy-plugins", rev = "0.12.2", subdirectory = "multitenant_provider"}

[tool.poetry.dev-dependencies]
black = "^24.8.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from typing import Optional, Union, List

from aries_cloudagent.core.profile import ProfileSession
from aries_cloudagent.ledger.base import LOGGER
from aries_cloudagent.messaging.models.base_record import BaseRecord, BaseRecordSchema
from aries_cloudagent.messaging.util import datetime_to_str, str_to_datetime
from aries_cloudagent.messaging.valid import UUIDFour
Expand Down Expand Up @@ -357,12 +356,19 @@ async def soft_delete(self, session: ProfileSession):
Soft delete the tenant record by setting its state to 'deleted'.
Note: This method should be called on an instance of the TenantRecord.
"""
# Delete api records
recs = await TenantAuthenticationApiRecord.query_by_tenant_id(
session, self.tenant_id
)
for rec in recs:
if rec.tenant_id == self.tenant_id:
await rec.delete_record(session)

if self.state != self.STATE_DELETED:
self.state = self.STATE_DELETED
self.deleted_at = datetime_to_str(datetime.utcnow())
await self.save(session, reason="Soft delete")


async def restore_deleted(self, session: ProfileSession):
"""
Un-soft-delete the tenant record by setting its state to 'active'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from aries_cloudagent.version import __version__
from aries_cloudagent.wallet.error import WalletSettingsError
from aries_cloudagent.wallet.models.wallet_record import WalletRecord
from multitenant_provider.v1_0.routes import plugin_wallet_create_token
from marshmallow import fields, validate

from . import TenantManager
Expand Down Expand Up @@ -522,6 +523,30 @@ async def tenant_create_token(request: web.BaseRequest):
return web.json_response({"token": token})


@docs(
tags=["multitenancy"],
summary="Get auth token for a subwallet (innkeeper plugin override)",
)
@request_schema(CreateWalletTokenRequestSchema)
@response_schema(CreateWalletTokenResponseSchema(), 200, description="")
@error_handler
async def tenant_wallet_create_token(request: web.BaseRequest):
context: AdminRequestContext = request["context"]
wallet_id = request.match_info["wallet_id"]

mgr = context.inject(TenantManager)
profile = mgr.profile

# Tenants must always be fetch by their wallet id.
async with profile.session() as session:
rec = await TenantRecord.query_by_wallet_id(session, wallet_id)
LOGGER.debug("when creating token ", rec)
if rec.state == TenantRecord.STATE_DELETED:
raise web.HTTPUnauthorized(reason="Tenant is disabled")

return await plugin_wallet_create_token(request)


@docs(
tags=[SWAGGER_CATEGORY],
)
Expand Down Expand Up @@ -1029,8 +1054,26 @@ async def register(app: web.Application):
"/multitenancy/reservations/{reservation_id}/check-in", tenant_checkin
),
web.post("/multitenancy/tenant/{tenant_id}/token", tenant_create_token),
web.post(
"/multitenancy/wallet/{wallet_id}/token", tenant_wallet_create_token
),
]
)
# Find the endpoint for token creation that already exists and
# override it
for r in app.router.routes():
if r.method == "POST":
if (
r.resource
and r.resource.canonical == "/multitenancy/wallet/{wallet_id}/token"
):
LOGGER.info(
f"found route: {r.method} {r.resource.canonical} ({r.handler})"
)
LOGGER.info(f"... replacing current handler: {r.handler}")
r._handler = tenant_wallet_create_token
LOGGER.info(f"... with new handler: {r.handler}")
has_wallet_create_token = True
# routes that require a tenant token for the innkeeper wallet/tenant/agent.
# these require not only a tenant, but it has to be the innkeeper tenant!
app.add_routes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from aries_cloudagent.messaging.models.openapi import OpenAPISchema
from marshmallow import fields

from .models import ReservationRecord, TenantAuthenticationApiRecord
from .models import ReservationRecord, TenantAuthenticationApiRecord, TenantRecord


from . import TenantManager
Expand Down Expand Up @@ -114,7 +114,9 @@ async def refresh_registration_token(reservation_id: str, manager: TenantManager
)
except Exception as err:
LOGGER.error("Failed to retrieve reservation: %s", err)
raise ReservationException("Could not retrieve reservation record.") from err
raise ReservationException(
"Could not retrieve reservation record."
) from err

if reservation.state != ReservationRecord.STATE_APPROVED:
raise ReservationException("Only approved reservations can refresh tokens.")
Expand All @@ -140,7 +142,8 @@ async def refresh_registration_token(reservation_id: str, manager: TenantManager

LOGGER.info("Refreshed token for reservation %s", reservation_id)

return _pwd
return _pwd


def generate_api_key_data():
_key = str(uuid.uuid4().hex)
Expand All @@ -156,6 +159,8 @@ def generate_api_key_data():


async def create_api_key(rec: TenantAuthenticationApiRecord, manager: TenantManager):
if rec.state == TenantRecord.STATE_DELETED:
raise ValueError("Tenant is disabled")
async with manager.profile.session() as session:
_key, _salt, _hash = generate_api_key_data()
rec.api_key_token_salt = _salt.decode("utf-8")
Expand Down
2 changes: 1 addition & 1 deletion services/aca-py/ngrok-wait.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ exec aca-py start \
--wallet-storage-config "{\"url\":\"${POSTGRESQL_HOST}:5432\",\"max_connections\":5, \"wallet_scheme\":\"${TRACTION_ACAPY_WALLET_SCHEME}\"}" \
--wallet-storage-creds "{\"account\":\"${POSTGRESQL_USER}\",\"password\":\"${POSTGRESQL_PASSWORD}\",\"admin_account\":\"${POSTGRESQL_USER}\",\"admin_password\":\"${POSTGRESQL_PASSWORD}\"}" \
--admin "0.0.0.0" ${TRACTION_ACAPY_ADMIN_PORT} \
--plugin multitenant_provider.v1_0 \
--plugin traction_plugins.traction_innkeeper.v1_0 \
--plugin basicmessage_storage.v1_0 \
--plugin connection_update.v1_0 \
--plugin multitenant_provider.v1_0 \
--plugin rpc.v1_0 \
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
{{ $t('common.deleted') }}
</span>
<RestoreTenant :id="data.tenant_id" :name="data.tenant_name" />
<DeleteTenant :tenant="data" unsuspendable />
</div>
</template>
</Column>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
{{ $t('tenants.settings.permanentDelete') }}
</label>
</div>
<div class="flex items-center my-2">
<div v-if="!props.unsuspendable" class="flex items-center my-2">
<RadioButton v-model="perminant" value="Soft" />
<label class="ml-2" for="">
{{ $t('tenants.settings.softDelete') }}
Expand Down Expand Up @@ -67,6 +67,7 @@ type DeletionAPI = 'Innkeeper' | 'Tenant';
const props = defineProps<{
tenant: TenantRecord;
api: DeletionAPI;
unsuspendable?: boolean;
}>();
const emit = defineEmits(['closed', 'success']);
Expand All @@ -82,12 +83,11 @@ const displayWarning = computed(() => {
if (perminant.value == 'Hard') return true;
else return false;
});
// toast.info("hello");
const toast = useToast();
type DeletionType = 'Hard' | 'Soft';
const perminant: Ref<DeletionType> = ref('Soft');
const perminant: Ref<DeletionType> = ref(props.unsuspendable ? 'Hard' : 'Soft');
const tenantDelete = async () => {
const tenantStore = useTenantStore();
Expand Down Expand Up @@ -119,7 +119,14 @@ async function handleDelete() {
await innkeeperDelete();
}
toast.success(
t('tenants.settings.confirmDeletionSuccess', [props.tenant.tenant_name])
t('tenants.settings.confirmDeletionSuccess', [
props.tenant.tenant_name,
t(
perminant.value == 'Hard'
? 'tenants.settings.deleted'
: 'tenants.settings.suspended'
),
])
);
emit('success');
emit('closed');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
>
<ConfirmTenantDeletion
:tenant="props.tenant"
:unsuspendable="props.unsuspendable"
api="Innkeeper"
@success="$emit('success')"
@closed="handleClose"
Expand All @@ -38,6 +39,7 @@ defineEmits(['success']);
const props = defineProps<{
tenant: TenantRecord;
unsuspendable?: boolean;
}>();
const displayModal = ref(false);
Expand Down
6 changes: 4 additions & 2 deletions services/tenant-ui/frontend/src/plugins/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,10 @@
"canRegisterDid": "Tenant allowed to register public DID",
"confirmDeletion": "To confirm, type \"{tenantName}\" in the box below",
"confirmDeletionIncorrect": "Incorrect tenant name. Please confirm the correct name before deletion.",
"confirmDeletionSuccess": "Tenant {0} successfully marked as deleted",
"deleteTenant": "Delete Tenant",
"confirmDeletionSuccess": "Tenant {0} successfully marked as {1}",
"suspended": "suspended",
"deleted": "deleted",
"deleteTenant": "Suspend Tenant",
"permanentDelete": "Permanently Delete",
"softDelete": "Suspend Tenant",
"tenantDeletionWarning": ": This will delete all data associated with this tenant.",
Expand Down

0 comments on commit c39fceb

Please sign in to comment.