Skip to content

Commit 5057007

Browse files
committed
Add JLINC link implementation for processing vCons through the JLINC API
- Introduced a new module for handling vCon processing, including authentication and communication with JLINC API and audit server. - Added comprehensive error handling and logging for API interactions. - Created a README file detailing configuration and process flow for the JLINC link.
1 parent 1404092 commit 5057007

File tree

2 files changed

+243
-0
lines changed

2 files changed

+243
-0
lines changed

server/links/jlinc/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# JLINC Link
2+
3+
This link processes vCons through the JLINC API and sends them to the JLINC audit server.
4+
5+
## Configuration
6+
7+
The JLINC link requires the following configuration options:
8+
9+
```yaml
10+
jlinc:
11+
module: links.jlinc
12+
ingress-lists: []
13+
egress-lists: []
14+
options:
15+
userDid: "string" # The user's DID
16+
userSigningKey: "string" # The user's signing key
17+
auditServerUrl: ["http://jlinc-archive-server:8081"] # List of JLINC audit server URLs to try
18+
apiServerUrl: ["http://jlinc-api-server:9090"] # List of JLINC API server URLs to try
19+
```
20+
21+
## Process
22+
23+
1. Retrieves the vCon from Redis
24+
2. Sends the vCon to the JLINC API server for processing
25+
3. Sends the processed vCon to the JLINC audit server
26+
4. Returns success if both operations succeed, failure otherwise
27+
28+
## Error Handling
29+
30+
The link will try all provided API server URLs and audit server URLs in order until successful. If all attempts fail, the link will return a failure status.

server/links/jlinc/__init__.py

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
from lib.logging_utils import init_logger
2+
from lib.vcon_redis import VconRedis
3+
import requests
4+
import json
5+
6+
logger = init_logger(__name__)
7+
8+
# Set the auth token
9+
auth_token = None
10+
# Rember to set the expiration time of the auth token
11+
auth_token_expiration = None
12+
13+
default_options = {
14+
"event_type": "vcon_created",
15+
"userDid": {
16+
"id": "did:jlinc:fedid-test.jlinc.io:A7bUH0oJlp2dFe0AYkMDSXE2jgRtocTl6YEGOLly_UI",
17+
"version": "2.0",
18+
"@context": [
19+
"https://www.w3.org/ns/did/v1",
20+
"https://didspec.jlinc.io/v2/ctx.jsonld"
21+
],
22+
"verificationMethod": [
23+
{
24+
"id": "#db40a8ea3d8756aa62063521c3f71f43",
25+
"type": "device",
26+
"controller": "did:jlinc:fedid-test.jlinc.io:A7bUH0oJlp2dFe0AYkMDSXE2jgRtocTl6YEGOLly_UI",
27+
"key": "2aij8ScGJfhrSjD6RiXEtWuIMz1Yx6NFxRjFVoHw4qM",
28+
"created": "2025-02-17T13:33:53Z",
29+
"deactivated": None
30+
}
31+
],
32+
"capabilityDelegation": [],
33+
"service": [
34+
{
35+
"id": "did:jlinc:fedid-test.jlinc.io:5c7ZbnC5DRS5ACYpBI2IJXaJVUsnSP5L5792GzNGFzo",
36+
"type": "login",
37+
"serviceEndpoint": "http://fedid-test.jlinc.io:8888"
38+
}
39+
],
40+
"created": "2025-02-17T13:33:53Z",
41+
"updated": "2025-02-17T13:33:53Z",
42+
"deactivated": False,
43+
"shortName": "user-nzilrt7o@fedid-test.jlinc.io",
44+
"recoveryHash": "e2bcf75f75f8f616c75622b8e2ede08b47ee781665514988"
45+
},
46+
"userSigningKey": "8h2VF5b79hkpOjqOISCrc3Suk12Gwo02HHfDrA3yqC3Gm3zNXydH6vGw8dmRc_SHu0iWtNmfrdyl7cjWVDJr8QP",
47+
"auditServerUrls": ["http://jlinc-archive-server:8081"],
48+
"apiServerUrl": "http://jlinc-api-server:9090",
49+
"agreementId": '23b3768b-5cec-4e01-bd95-e02a334f0943',
50+
"recipientId": "did:jlinc:agent:23b3768b-5cec-4e01-bd95-e02a334f0943",
51+
"authorization": {
52+
"did-id": "did:jlinc:did.jlinc.io:MzA3ZDNiMTVlNDE5NDk1YjU0MzVmYmUyM",
53+
"api-key": "NjY0NTBiN2M1MWQwOTU3ZGJkZTYzMjI1N"
54+
}
55+
}
56+
57+
def run(vcon_uuid, link_name, opts=default_options):
58+
"""JLINC link that processes vCons through the JLINC API and sends them to the audit server.
59+
60+
Args:
61+
vcon_uuid: UUID of the vCon to process
62+
link_name: Name of this link instance
63+
opts: Link options containing:
64+
userDid: The user's DID
65+
userSigningKey: The user's signing key
66+
apiServerUrl: JLINC API server URLs
67+
auditServerUrls: List of JLINC audit server URLs to try
68+
agreementId: The agreement ID to use for the vCon
69+
recipientDidId: The DID of the agent to use for the vCon
70+
71+
Returns:
72+
True if the vCon was processed successfully
73+
False otherwise
74+
"""
75+
logger.info(f"Running {link_name} link on vCon {vcon_uuid}")
76+
77+
# Get vCon data from Redis
78+
vcon_redis = VconRedis()
79+
vcon_obj = vcon_redis.get_vcon(vcon_uuid)
80+
if not vcon_obj:
81+
logger.error(f"Could not get vCon {vcon_uuid} from Redis")
82+
return False
83+
84+
vcon_dict = vcon_obj.to_dict()
85+
86+
# Get the auth token from the JLINC API
87+
global auth_token, auth_token_expiration
88+
89+
if auth_token is None:
90+
# Get the auth token from the JLINC API
91+
auth_url = f"{opts['apiServerUrl']}/api/v1/auth"
92+
response = requests.post(auth_url, headers={
93+
"did-id" : opts["authorization"]["did-id"],
94+
"api-key" : opts["authorization"]["api-key"]
95+
})
96+
if response.status_code == 200:
97+
auth_token = response.json()["token"]
98+
else:
99+
logger.warning(f"Failed to get auth token from JLINC API at {auth_url}. Status: {response}")
100+
raise Exception(f"Failed to get auth token from JLINC API at {auth_url}. Status: {response.status_code}")
101+
102+
try:
103+
api_url = opts['apiServerUrl']
104+
audit_urls = opts['auditServerUrls']
105+
logger.info(f"Processing vCon {vcon_uuid} through JLINC API at {api_url}")
106+
107+
request_data = {
108+
"type": "data",
109+
"senderId": opts["userDid"]["id"],
110+
"recipientId": opts["recipientId"],
111+
"agreementId": opts["agreementId"],
112+
"data": {
113+
"event_type": opts["event_type"],
114+
"vcon_uuid": vcon_uuid,
115+
"vcon_hash": vcon_obj.hash,
116+
"created_at": vcon_obj.created_at
117+
}
118+
}
119+
120+
response = requests.post(
121+
f"{api_url}/api/v1/event/create",
122+
headers={"Authorization": f"Bearer {auth_token}"},
123+
json=request_data
124+
)
125+
126+
if response.status_code == 200:
127+
processed_vcon = response.json()
128+
logger.info(f"Successfully processed vCon {vcon_uuid} through JLINC API at {api_url}")
129+
logger.info(f"Processed vCon: {processed_vcon}")
130+
else:
131+
logger.warning(f"Failed to process vCon {vcon_uuid} through JLINC API at {api_url}. Status: {response.status_code}")
132+
raise Exception(f"Failed to process vCon {vcon_uuid} through JLINC API at {api_url}. Status: {response.status_code}")
133+
134+
# Make the signing request, like the create event request
135+
request_data = {
136+
"event": processed_vcon,
137+
"didDoc": opts["userDid"],
138+
"signingKey": opts["userSigningKey"],
139+
"signingPublicKey": opts["userDid"]["verificationMethod"][0]["key"]
140+
}
141+
response = requests.post(
142+
f"{api_url}/api/v1/event/sign",
143+
headers={"Authorization": f"Bearer {auth_token}"},
144+
json=request_data
145+
)
146+
147+
if response.status_code == 200:
148+
logger.info(f"Successfully signed vCon {vcon_uuid} through JLINC API at {api_url}")
149+
signedEvent = response.json()
150+
else:
151+
logger.warning(f"Failed to sign vCon {vcon_uuid} through JLINC API at {api_url}. Status: {response.status_code}")
152+
raise Exception(f"Failed to sign vCon {vcon_uuid} through JLINC API at {api_url}. Status: {response.status_code}")
153+
154+
155+
# Send processed vCon to JLINC audit server
156+
try:
157+
response = requests.post(
158+
f"{api_url}/api/v1/audit/create",
159+
headers={"Authorization": f"Bearer {auth_token}"},
160+
json=signedEvent
161+
)
162+
163+
if response.status_code == 200:
164+
logger.info(f"Successfully sent vCon {vcon_uuid} to JLINC api server at {api_url}")
165+
event_audit_record = response.json()
166+
else:
167+
logger.warning(f"Failed to send vCon {vcon_uuid} to JLINC api server at {api_url}. Status: {response.status_code}")
168+
169+
request_data = {
170+
"audit": event_audit_record,
171+
"didDoc": opts["userDid"],
172+
"signingKey": opts["userSigningKey"],
173+
"signingPublicKey": opts["userDid"]["verificationMethod"][0]["key"]
174+
}
175+
176+
# Make the signing request, like the create event request
177+
response = requests.post(
178+
f"{api_url}/api/v1/audit/sign",
179+
headers={"Authorization": f"Bearer {auth_token}"},
180+
json=request_data
181+
)
182+
183+
if response.status_code == 200:
184+
logger.info(f"Successfully signed vCon {vcon_uuid} to JLINC api server at {api_url}")
185+
signed_audit_record = response.json()
186+
else:
187+
logger.warning(f"Failed to sign vCon {vcon_uuid} to JLINC api server at {api_url}. Status: {response.status_code}")
188+
189+
for audit_url in audit_urls:
190+
try:
191+
response = requests.post(
192+
f"{audit_url}/api/v1/audit/put",
193+
json=signed_audit_record
194+
)
195+
if response.status_code == 200:
196+
logger.info(f"Successfully sent vCon {vcon_uuid} to JLINC audit server at {audit_url}")
197+
event_audit_record = response.json()
198+
else:
199+
logger.warning(f"Failed to send vCon {vcon_uuid} to JLINC audit server at {audit_url}. Status: {response.status_code}")
200+
continue
201+
except Exception as e:
202+
logger.warning(f"Error connecting to JLINC audit server at {audit_url}: {e}")
203+
204+
except Exception as e:
205+
logger.warning(f"Error connecting to JLINC api server at {api_url}: {e}")
206+
207+
# Send the event audit record to the audit server
208+
except Exception as e:
209+
logger.warning(f"Error connecting to JLINC API at {api_url}: {e}")
210+
211+
# Successfully processed and audited
212+
logger.info(f"Successfully completed JLINC processing for vCon {vcon_uuid}")
213+
return True

0 commit comments

Comments
 (0)