Skip to content

Commit

Permalink
[FIX] - Update to vcdm2.0, Add DI Proof and remove WIP code (#3276)
Browse files Browse the repository at this point in the history
* attestation type and use published vocab

* need tupe

* update dcc construction and library to dcc 0.3.10

* update test

* test

* remove/update logging

* ensure query doesn't include regional mines (yet

* make context files configurable.

based on UNTPDCC and BCMinesActPermitCredential versions

* real context extension

* remove type not defined in context files.

update context reference to 0.5.0

* add schema file and config, updated to match 0.1.0 of models package

* nullable but not defaulted.

* same with product

* same for ca

* link to credential, not orgbook identifer.

* real tdw registry, and schema extension

* Define verification method, strip did

stop using sovrin did, just use did:web from tdw server.

* add permit_number, remove name.

* extended class needs extended type

* name is required in model (But not in spec)

remove hardcode from id path

* bc prefix is removed

* vcdm 2.0 updates.

name is included, issuanceDate is now validFrom.

* update to use new DataIntegrity Proof

DI proofs are going to be required by orgbook, but maybe not others. removed other WIP code with different endpoints

* remove dup line from merge

* remove dup lines from merge.

* key 1 is different, signing key is now key-02-multikey

* use mine_no ad factility identifier

* remove unused urls
  • Loading branch information
Jsyro authored Oct 23, 2024
1 parent a29e139 commit c3f88ad
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 134 deletions.
3 changes: 2 additions & 1 deletion services/core-api/.env-example
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ TRACTION_TENANT_ID=MISSING_TENANT_ID
TRACTION_WALLET_API_KEY=MISSING_API_KEY
CRED_DEF_ID_MINES_ACT_PERMIT=CRED_DEF_ID_FOR_MINES_ACT_PERMIT
TRACTION_WEBHOOK_X_API_KEY=MISSING_TRACTION_WEBHOOK_X_API_KEY
CHIEF_PERMITTING_OFFICER_DID_WEB_VERIFICATION_METHOD="did:web:registry-dev.apps.silver.devops.gov.bc.ca:mines-act:chief-permitting-officer#key-01-multikey"
# This is manually created in traction maping kid to verkey in did:doc @ POST to <TRACTION>/wallets/keys
CHIEF_PERMITTING_OFFICER_DID_WEB_VERIFICATION_METHOD="did:web:registry-dev.apps.silver.devops.gov.bc.ca:mines-act:chief-permitting-officer#key-02-multikey"
UNTP_DIGITAL_CONFORMITY_CREDENTIAL_CONTEXT=https://test.uncefact.org/vocabulary/untp/dcc/0.5.0/
UNTP_DIGITAL_CONFORMITY_CREDENTIAL_SCHEMA=https://test.uncefact.org/vocabulary/untp/dcc/untp-dcc-schema-0.5.0.json
UNTP_BC_MINES_ACT_PERMIT_CONTEXT=https://bcgov.github.io/digital-trust-toolkit/contexts/BCMinesActPermit/v1.jsonld
Expand Down
48 changes: 17 additions & 31 deletions services/core-api/app/api/services/traction_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@
from app.api.utils.feature_flag import Feature, is_feature_enabled

traction_token_url = Config.TRACTION_HOST + "/multitenancy/tenant/" + Config.TRACTION_TENANT_ID + "/token"

traction_oob_create_invitation = Config.TRACTION_HOST + "/out-of-band/create-invitation"
traction_connections = Config.TRACTION_HOST + "/connections"

traction_offer_credential_V1 = Config.TRACTION_HOST + "/issue-credential/send-offer"
traction_offer_credential_V2 = Config.TRACTION_HOST + "/issue-credential-2.0/send-offer"
revoke_credential_url = Config.TRACTION_HOST + "/revocation/revoke"
fetch_credential_exchanges = Config.TRACTION_HOST + "/issue-credential/records"
traction_deprecated_jsonld_sign = Config.TRACTION_HOST + "/jsonld/sign"
traction_sign_jsonld_credential = Config.TRACTION_HOST + "/vc/credentials/issue"

traction_get_current_indy_did = Config.TRACTION_HOST + "/wallet/did/public"
traction_get_did = Config.TRACTION_HOST + "/wallet/did"

traction_vc_di_add_proof = Config.TRACTION_HOST + "/vc/di/add-proof"


def traction_issue_credential_problem_report(cred_ex_id: str):
return Config.TRACTION_HOST + f"/issue-credential/records/{cred_ex_id}/problem-report"
Expand Down Expand Up @@ -186,42 +189,25 @@ def fetch_current_public_did(self):
assert get_resp.status_code == 200, f"fetch_resp={get_resp.json()}"
return get_resp.json()["result"]

def sign_jsonld_credential_deprecated(
self,
verificationMethod: str,
verkey: str,
credential: BaseModel,
) -> dict:

#TODO update to resolve the verkey from the verification method and use that. Acapy only knows the verkey as a local did/keypair

options = {"verificationMethod": verificationMethod, "proofPurpose": "assertionMethod"}

class Payload(BaseModel):
doc: dict
verkey: str
def sign_add_data_integrity_proof(self, verificationMethod: str, credential: BaseModel):
options = {
"cryptosuite": "eddsa-jcs-2022",
"proofPurpose": "assertionMethod",
"type": "DataIntegrityProof",
"verificationMethod": verificationMethod
}

payload = Payload(doc={"options": options, "credential": credential}, verkey=verkey)
payload = {
"document": credential.model_dump(by_alias=True, exclude_none=True, mode="json"),
"options": options
}

post_resp = requests.post(
traction_deprecated_jsonld_sign,
json=payload.model_dump(by_alias=True, exclude_none=True, mode="json"),
headers=self.get_headers())
assert post_resp.status_code == 200, f"post_resp={post_resp.__dict__}"
traction_vc_di_add_proof, json=payload, headers=self.get_headers())
return post_resp.json()

def fetch_a_random_did_key(self):
get_resp = requests.get(
traction_get_did, params={"method": "key"}, headers=self.get_headers())
assert get_resp.status_code == 200, f"fetch_resp={get_resp.json()}"
return get_resp.json()["results"][0]

def sign_jsonld_credential(self, credential: dict, options: dict = {}):
payload = {
"options": options,
"credential": credential,
}
post_resp = requests.post(
traction_sign_jsonld_credential, json=payload, headers=self.get_headers())
assert post_resp
return post_resp.json()
34 changes: 13 additions & 21 deletions services/core-api/app/api/verifiable_credentials/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,27 @@


class UNTPCCMinesActPermit(cc.ConformityAttestation):
type: List[str] = ["ConformityAttestation, MinesActPermit"]
type: List[str] = ["ConformityAttestation", "MinesActPermit"]
permitNumber: str


#this should probably be imported from somewhere.
class W3CCred(BaseModel):
#based on VCDM 2.0. https://www.w3.org/TR/vc-data-model-2.0/
model_config = ConfigDict(
populate_by_name=True, json_encoders={datetime: lambda v: v.isoformat()})

context: List[Union[str, dict]] = Field(
alias="@context",
default=[
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/ns/credentials/v2",
Config.UNTP_DIGITAL_CONFORMITY_CREDENTIAL_CONTEXT,
Config.UNTP_BC_MINES_ACT_PERMIT_CONTEXT, {
"name": "https://schema.org/name"
}
Config.UNTP_BC_MINES_ACT_PERMIT_CONTEXT,
])
type: List[str]
issuer: Union[str, dict[str, str]]
# TODO: update to `validFrom` for vcdm 2.0 once available in aca-py/traction, which is an optional property
issuanceDate: str
validFrom: str
credentialSubject: UNTPCCMinesActPermit
credentialSchema: List[dict]

Expand Down Expand Up @@ -194,11 +193,10 @@ def process_all_untp_map_for_orgbook():
task_logger.info(f"public_verkey={public_verkey}")
# send to traction to be signed
for cred_payload, record in records:
signed_cred = traction_service.sign_jsonld_credential_deprecated(
Config.CHIEF_PERMITTING_OFFICER_DID_WEB_VERIFICATION_METHOD, public_verkey,
cred_payload)
signed_cred = traction_service.sign_add_data_integrity_proof(
Config.CHIEF_PERMITTING_OFFICER_DID_WEB_VERIFICATION_METHOD, cred_payload)
if signed_cred:
record.signed_credential = json.dumps(signed_cred["signed_doc"])
record.signed_credential = json.dumps(signed_cred["securedDocument"])
record.sign_date = datetime.now()
try:
record.save()
Expand Down Expand Up @@ -380,22 +378,16 @@ def produce_untp_cc_map_payload(cls, did: str, permit_amendment: PermitAmendment
facility = cc.Facility(
id=None,
name=permit_amendment.mine.mine_name,
registeredId="mine_no",
registeredId=permit_amendment.mine.mine_no,
locationInformation=
f'https://plus.codes/{plus_code_encode(permit_amendment.mine.latitude, permit_amendment.mine.longitude)}',
address=None,
IDverifiedByCAB=True)

#TODO, can CORE identify commodities by their UNCEFACT code?
products = [
cc.Product(
id=None,
name=c,
#TODO, can CORE identify commodities by their UNCEFACT code?
# id=c.uncefact_code?
# idScheme=base.IdentifierScheme(
# id="https://unstats.un.org/unsd/classifications/Econ/cpc",
# name="Central Product Classification (UNCEFACT)"),
IDverifiedByCAB=False) for c in permit_amendment.mine.commodities
cc.Product(id=None, name=c, IDverifiedByCAB=False)
for c in permit_amendment.mine.commodities
]

issue_date = permit_amendment.issue_date
Expand Down Expand Up @@ -449,7 +441,7 @@ def produce_untp_cc_map_payload(cls, did: str, permit_amendment: PermitAmendment
"VerifiableCredential", "DigitalConformityCredential", "BCMinesActPermitCredential"
],
issuer={"id": did},
issuanceDate=issuance_date_str, #vcdm1.1, will change to 'validFrom' in vcdm2.0
validFrom=issuance_date_str, #vcdm1.1, will change to 'validFrom' in vcdm2.0
credentialSubject=cred,
credentialSchema=[{
"id": Config.UNTP_DIGITAL_CONFORMITY_CREDENTIAL_CONTEXT,
Expand Down
6 changes: 2 additions & 4 deletions services/core-api/app/api/verifiable_credentials/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from app.api.verifiable_credentials.resources.vc_map import VerifiableCredentialMinesActPermitResource
from app.api.verifiable_credentials.resources.vc_map_detail import VerifiableCredentialCredentialExchangeResource
from app.api.verifiable_credentials.resources.vc_revocation import VerifiableCredentialRevocationResource
from app.api.verifiable_credentials.resources.w3c_map_credential_resource import W3CCredentialResource, W3CCredentialListResource, W3CCredentialDeprecatedResource, W3CCredentialUNTPResource
from app.api.verifiable_credentials.resources.w3c_map_credential_resource import W3CCredentialResource, W3CCredentialIssueResource

api = Namespace('verifiable-credentials', description='Variances actions/options')

Expand All @@ -24,6 +24,4 @@
'/<string:party_guid>/mines-act-permits/revoke')

api.add_resource(W3CCredentialResource, '/credentials/<string:vc_unsigned_hash>')
api.add_resource(W3CCredentialListResource, '/credentials/')
api.add_resource(W3CCredentialDeprecatedResource, '/credentials/deprecated/')
api.add_resource(W3CCredentialUNTPResource, '/untp-credentials')
api.add_resource(W3CCredentialIssueResource, '/credentials/issue')
Original file line number Diff line number Diff line change
Expand Up @@ -34,42 +34,7 @@ def get(self, vc_unsigned_hash: str):
vc_unsigned_hash, unsafe=True).signed_credential)


class W3CCredentialListResource(Resource, UserMixin):
parser = reqparse.RequestParser(trim=True)
parser.add_argument(
'permit_amendment_guid',
type=str,
help='GUID of the permit amendment.',
location='json',
store_missing=False)

@api.expect(parser)
@api.doc(
description=
"returns a signed w3c credential for a specific permit_amendment, using new aca-py endpoints, but cannot use public did."
)
@requires_any_of([EDIT_PARTY, MINESPACE_PROPONENT])
def post(self):
if not is_feature_enabled(Feature.VC_W3C):
raise ServiceUnavailable("This feature is not enabled.")

data = self.parser.parse_args()
permit_amendment = PermitAmendment.find_by_permit_amendment_guid(
data["permit_amendment_guid"])
traction_service = TractionService()
did_dict = traction_service.fetch_a_random_did_key()
private_did_key = did_dict["did"]
#private did:key: isn't that helpful, not available to third parties
credential_dict = VerifiableCredentialManager.produce_map_01_credential_payload(
private_did_key, permit_amendment)

signed_credential = traction_service.sign_jsonld_credential(credential_dict)
current_app.logger.warning("credential signed by did:key, not publicly verifiable" +
dumps(signed_credential))
return signed_credential["verifiableCredential"]


class W3CCredentialDeprecatedResource(Resource, UserMixin):
class W3CCredentialIssueResource(Resource, UserMixin):
parser = reqparse.RequestParser(trim=True)
parser.add_argument(
'permit_amendment_guid',
Expand Down Expand Up @@ -99,46 +64,8 @@ def post(self):
credential_dict = VerifiableCredentialManager.produce_map_01_credential_payload(
public_did, permit_amendment)

signed_credential = traction_service.sign_jsonld_credential_deprecated(
signed_credential = traction_service.sign_add_data_integrity_proof(
Config.CHIEF_PERMITTING_OFFICER_DID_WEB_VERIFICATION_METHOD, public_verkey,
credential_dict)
current_app.logger.warning(
"credential signed by did:indy, not by did:web and using deprecated acapy endpoints" +
dumps(signed_credential))
return signed_credential["signed_doc"]


class W3CCredentialUNTPResource(Resource, UserMixin):
parser = reqparse.RequestParser(trim=True)
parser.add_argument(
'permit_amendment_guid',
type=str,
help='GUID of the permit amendment.',
location='json',
store_missing=False)

@api.expect(parser)
@api.doc(
description=
"returns a UNTP Conformity Credential for specific permit_amendment using deprecated aca-py endpoint, but with DEV ONLY did:web"
)
@requires_any_of([EDIT_PARTY, MINESPACE_PROPONENT])
def post(self):
if not is_feature_enabled(Feature.VC_W3C):
raise ServiceUnavailable("This feature is not enabled.")

data = self.parser.parse_args()
permit_amendment = PermitAmendment.find_by_permit_amendment_guid(
data["permit_amendment_guid"])
if not permit_amendment:
raise BadRequest("Permit amendment not found")
traction_service = TractionService()
public_did_dict = traction_service.fetch_current_public_did()
public_did = Config.CHIEF_PERMITTING_OFFICER_DID_WEB
public_verkey = public_did_dict["verkey"]

credential = VerifiableCredentialManager.produce_untp_cc_map_payload(
public_did, permit_amendment)
signed_credential = traction_service.sign_jsonld_credential_deprecated(
Config.CHIEF_PERMITTING_OFFICER_DID_WEB_VERIFICATION_METHOD, public_verkey, credential)
return signed_credential["signed_doc"]
return signed_credential["securedDocument"]
2 changes: 1 addition & 1 deletion services/core-api/app/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,4 @@ def do_generate_table_command(table):
Example usage:
flask generate_table_migration mine_tailings_storage_facility
"""
generate_table_migration(table)
generate_table_migration(table)

0 comments on commit c3f88ad

Please sign in to comment.