Skip to content

Commit

Permalink
feat: digital credentials (#2287)
Browse files Browse the repository at this point in the history
* feat: devcontainer configuraton for vscode

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: hard code digital business card schema

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: hard code digital business card schema

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: issue credentials through Traction tenant

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* refactor: app initialization workflow

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: use out-of-band invitation for connecting

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: use v2.0 for issuing credential

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: web socket implmentation with flask-socketio

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: db migration script to enable revocation

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: revocation endpoint

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: replace endpoints

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* chore: fix linting errors

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* chore: update requirements

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* chore: update tests

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: traction token exchanger

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* chore: update workflow variables

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* chore: update workflow variables

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* refactor: ws cors setting is a config option

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* chore: fix linting errors

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* refactor: clean up init in digital credential service

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: endpoints to reset credential offers

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: credential id lookup table

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* feat: add business roles

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* chore: fix tests and linting

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* chore: fix tests

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* refactor: remove records from Traction on deletion

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* Revert "feat: web socket implmentation with flask-socketio"

This reverts commit 79a2631.

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* fix: port so it doesnt overlap with airplay server on OSX

Signed-off-by: Akiff Manji <amanji@petridish.dev>

* Revert "fix: port so it doesnt overlap with airplay server on OSX"

This reverts commit 9763a17.

Signed-off-by: Akiff Manji <amanji@petridish.dev>

---------

Signed-off-by: Akiff Manji <amanji@petridish.dev>
  • Loading branch information
amanji authored Nov 1, 2023
1 parent 65a9a4d commit 2976d11
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 47 deletions.
5 changes: 1 addition & 4 deletions legal-api/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,7 @@ tag: push ## tag image
# COMMANDS - Local #
#################################################################################
run: ## Run the project in local
. venv/bin/activate && python -m flask run -p 5050

run-websockets: ## Run the project in local with websockets
. venv/bin/activate && python -m gunicorn --threads 100 --bind :5050 wsgi
. venv/bin/activate && python -m flask run -p 5000

#################################################################################
# Self Documenting Commands #
Expand Down
14 changes: 7 additions & 7 deletions legal-api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ Flask-Moment==0.11.0
Flask-Pydantic==0.8.0
Flask-SQLAlchemy==2.5.1
Flask-Script==2.0.6
Flask-SocketIO==5.3.6
Flask==1.1.2
Jinja2==2.11.3
Mako==1.1.4
MarkupSafe==1.1.1
PyPDF2==1.26.0
SQLAlchemy-Continuum==1.3.13
SQLAlchemy-Utils==0.37.8
SQLAlchemy==1.4.44
Werkzeug==1.0.1
nest_asyncio
alembic==1.7.5
aniso8601==9.0.1
asyncio-nats-client==0.11.4
Expand All @@ -32,15 +31,11 @@ ecdsa==0.14.1
expiringdict==1.1.4
flask-jwt-oidc==0.3.0
flask-restx==0.3.0
git+https://github.com/bcgov/business-schemas.git@2.18.13#egg=registry_schemas
gunicorn==20.1.0
html-sanitizer==1.9.3
idna==2.10
itsdangerous==1.1.0
jsonschema==4.19.0
launchdarkly-server-sdk==7.1.0
minio==7.0.2
nest_asyncio
protobuf==3.15.8
psycopg2-binary==2.8.6
pyRFC3339==1.1
Expand All @@ -54,11 +49,16 @@ python-dotenv==0.17.1
python-editor==1.0.4
python-jose==3.2.0
pytz==2021.1
reportlab==3.6.12
requests==2.25.1
rsa==4.7.2
semver==2.13.0
sentry-sdk==1.20.0
six==1.15.0
strict-rfc3339==0.7
urllib3==1.26.11
minio==7.0.2
PyPDF2==1.26.0
reportlab==3.6.12
html-sanitizer==1.9.3
git+https://github.com/bcgov/business-schemas.git@2.18.13#egg=registry_schemas

6 changes: 0 additions & 6 deletions legal-api/src/legal_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from registry_schemas.flask import SchemaServices # noqa: I001

from legal_api import config, models
from legal_api.extensions import socketio
from legal_api.models import db
from legal_api.resources import endpoints
from legal_api.schemas import rsbc_schemas
Expand Down Expand Up @@ -68,11 +67,6 @@ def create_app(run_mode=os.getenv('FLASK_ENV', 'production')):

register_shellcontext(app)

ws_allowed_origins = app.config.get('WS_ALLOWED_ORIGINS', [])
if isinstance(ws_allowed_origins, str) and ws_allowed_origins != '*':
ws_allowed_origins = ws_allowed_origins.split(',')
socketio.init_app(app, cors_allowed_origins=ws_allowed_origins)

return app


Expand Down
25 changes: 0 additions & 25 deletions legal-api/src/legal_api/extensions.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from flask import Blueprint, _request_ctx_stack, current_app, jsonify, request
from flask_cors import cross_origin

from legal_api.extensions import socketio
from legal_api.models import (
Business,
CorpType,
Expand Down Expand Up @@ -104,6 +103,11 @@ def delete_connection(identifier, connection_id):
if not connection:
return jsonify({'message': f'{identifier} connection not found.'}), HTTPStatus.NOT_FOUND

try:
digital_credentials.remove_connection_record(connection_id=connection.connection_id)
except Exception:
return jsonify({'message': 'Failed to remove connection record.'}), HTTPStatus.INTERNAL_SERVER_ERROR

connection.delete()
return jsonify({'message': 'Connection has been deleted.'}), HTTPStatus.OK

Expand All @@ -121,6 +125,11 @@ def delete_active_connection(identifier):
if not connection:
return jsonify({'message': f'{identifier} active connection not found.'}), HTTPStatus.NOT_FOUND

try:
digital_credentials.remove_connection_record(connection_id=connection.connection_id)
except Exception:
return jsonify({'message': 'Failed to remove connection record.'}), HTTPStatus.INTERNAL_SERVER_ERROR

connection.delete()
return jsonify({'message': 'Connection has been deleted.'}), HTTPStatus.OK

Expand Down Expand Up @@ -243,6 +252,11 @@ def delete_credential(identifier, credential_id):
if not issued_credential:
return jsonify({'message': f'{identifier} issued credential not found.'}), HTTPStatus.NOT_FOUND

try:
digital_credentials.remove_credential_exchange_record(issued_credential.credential_exchange_id)
except Exception:
return jsonify({'message': 'Failed to remove credential exchange record.'}), HTTPStatus.INTERNAL_SERVER_ERROR

issued_credential.delete()
return jsonify({'message': 'Credential has been deleted.'}), HTTPStatus.OK

Expand All @@ -265,7 +279,6 @@ def webhook_notification(topic_name: str):
connection.connection_state = json_input['state']
connection.is_active = True
connection.save()
socketio.emit('connections', connection.json)
elif topic_name == 'issuer_cred_rev':
issued_credential = DCIssuedCredential.find_by_credential_exchange_id(json_input['cred_ex_id'])
if issued_credential and json_input['state'] == 'issued':
Expand All @@ -278,7 +291,6 @@ def webhook_notification(topic_name: str):
issued_credential.date_of_issue = datetime.utcnow()
issued_credential.is_issued = True
issued_credential.save()
socketio.emit('issue_credential_v2_0', issued_credential.json)
except Exception as err:
current_app.logger.error(err)
raise err
Expand Down
24 changes: 24 additions & 0 deletions legal-api/src/legal_api/services/digital_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,30 @@ def revoke_credential(self, connection_id, cred_rev_id: str, rev_reg_id: str) ->
self.app.logger.error(err)
return None

@requires_traction_auth
def remove_connection_record(self, connection_id: str) -> Optional[dict]:
"""Delete a connection."""
try:
response = requests.delete(self.api_url + '/connections/' + connection_id,
headers=self._get_headers())
response.raise_for_status()
return response.json()
except Exception as err:
self.app.logger.error(err)
return None

@requires_traction_auth
def remove_credential_exchange_record(self, cred_ex_id: str) -> Optional[dict]:
"""Delete a credential exchange."""
try:
response = requests.delete(self.api_url + '/issue-credential-2.0/records/' + cred_ex_id,
headers=self._get_headers())
response.raise_for_status()
return response.json()
except Exception as err:
self.app.logger.error(err)
return None

def _get_headers(self) -> dict:
return {
'Content-Type': 'application/json',
Expand Down
3 changes: 1 addition & 2 deletions legal-api/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
import os

from legal_api import create_app
from legal_api.extensions import socketio

# Openshift s2i expects a lower case name of application
application = create_app() # pylint: disable=invalid-name

if __name__ == "__main__":
server_port = os.environ.get('PORT', '8080')
socketio.run(application, debug=False, port=server_port, host='0.0.0.0')
application.run(debug=False, port=server_port, host='0.0.0.0')

0 comments on commit 2976d11

Please sign in to comment.