|
67 | 67 | from .messages.json_ld_problem_report import JSONLDProblemReport, JSONLDProblemReportReason
|
68 | 68 | from .messages.read_all_data_agreement_template import ReadAllDataAgreementTemplateMessage
|
69 | 69 | from .messages.read_all_data_agreement_template_response import ReadAllDataAgreementTemplateResponseMessage
|
| 70 | +from .messages.data_controller_details import DataControllerDetailsMessage |
| 71 | +from .messages.data_controller_details_response import DataControllerDetailsResponseMessage |
70 | 72 |
|
71 | 73 | from .models.data_agreement_model import DATA_AGREEMENT_V1_SCHEMA_CONTEXT, DataAgreementEventSchema, DataAgreementV1, DataAgreementPersonalData, DataAgreementV1Schema
|
72 | 74 | from .models.read_data_agreement_model import ReadDataAgreementBody
|
|
85 | 87 | from .models.data_agreement_qr_code_initiate_model import DataAgreementQrCodeInitiateBody
|
86 | 88 | from .models.json_ld_processed_response_model import JSONLDProcessedResponseBody
|
87 | 89 | from .models.json_ld_processed_model import JSONLDProcessedBody
|
| 90 | +from .models.data_controller_model import DataController, DataControllerSchema |
88 | 91 |
|
89 | 92 | from .utils.diddoc import DIDDoc
|
90 | 93 | from .utils.did.mydata_did import DIDMyData
|
@@ -140,6 +143,9 @@ class ADAManager:
|
140 | 143 | # Temporary record for keeping personal data of unpublished (or draft) data agreements
|
141 | 144 | RECORD_TYPE_TEMPORARY_DATA_AGREEMENT_PERSONAL_DATA = "temporary_data_agreement_personal_data"
|
142 | 145 |
|
| 146 | + # Record for data controller details |
| 147 | + RECORD_TYPE_DATA_CONTROLLER_DETAILS = "data_controller_details" |
| 148 | + |
143 | 149 | DATA_AGREEMENT_RECORD_TYPE = "dataagreement_record"
|
144 | 150 |
|
145 | 151 | def __init__(self, context: InjectionContext) -> None:
|
@@ -3862,7 +3868,6 @@ async def create_invitation(
|
3862 | 3868 | # Make request to iGrant.io organisation detail endpoint
|
3863 | 3869 | async with aiohttp.ClientSession(headers=request_headers) as session:
|
3864 | 3870 | async with session.get(igrantio_organisation_detail_url) as resp:
|
3865 |
| - print(await resp.text()) |
3866 | 3871 | if resp.status == 200:
|
3867 | 3872 | jresp = await resp.json()
|
3868 | 3873 |
|
@@ -4093,3 +4098,181 @@ async def send_read_all_data_agreement_template_message(self, conn_id: str) -> N
|
4093 | 4098 | # Send JSONLD Processed Message
|
4094 | 4099 | if responder:
|
4095 | 4100 | await responder.send_reply(read_all_data_agreement_template_message, connection_id=connection_record.connection_id)
|
| 4101 | + |
| 4102 | + |
| 4103 | + async def fetch_org_details_from_igrantio(self)-> str: |
| 4104 | + """ |
| 4105 | + Fetch org details from iGrant.io. |
| 4106 | + """ |
| 4107 | + |
| 4108 | + # fetch iGrant.io config from os environment |
| 4109 | + igrantio_config = await self.fetch_igrantio_config_from_os_environ() |
| 4110 | + |
| 4111 | + # Construct iGrant.io organisation detail endpoint URL |
| 4112 | + igrantio_organisation_detail_url = f"{igrantio_config['igrantio_endpoint_url']}/v1/organizations/{igrantio_config['igrantio_org_id']}" |
| 4113 | + |
| 4114 | + # Construct request headers |
| 4115 | + request_headers = { |
| 4116 | + "Authorization": f"ApiKey {igrantio_config['igrantio_org_api_key']}" |
| 4117 | + } |
| 4118 | + |
| 4119 | + data_controller = json.dumps({}) |
| 4120 | + |
| 4121 | + async with aiohttp.ClientSession(headers=request_headers) as session: |
| 4122 | + async with session.get(igrantio_organisation_detail_url) as resp: |
| 4123 | + if resp.status == 200: |
| 4124 | + jresp = await resp.json() |
| 4125 | + |
| 4126 | + if "Organization" in jresp: |
| 4127 | + organization_details = jresp["Organization"] |
| 4128 | + |
| 4129 | + exclude_keys = [ |
| 4130 | + "BillingInfo", |
| 4131 | + "Admins", |
| 4132 | + "HlcSupport", |
| 4133 | + "DataRetention", |
| 4134 | + "Enabled", |
| 4135 | + "Subs" |
| 4136 | + ] |
| 4137 | + |
| 4138 | + for exclude_key in exclude_keys: |
| 4139 | + organization_details.pop(exclude_key, None) |
| 4140 | + |
| 4141 | + data_controller = DataController( |
| 4142 | + organisation_id=organization_details["ID"], |
| 4143 | + organisation_name=organization_details["Name"], |
| 4144 | + cover_image_url=organization_details["CoverImageURL"] + "/web", |
| 4145 | + logo_image_url=organization_details["LogoImageURL"] + "/web", |
| 4146 | + location=organization_details["Location"], |
| 4147 | + organisation_type=organization_details["Type"]["Type"], |
| 4148 | + description=organization_details["Description"], |
| 4149 | + policy_url=organization_details["PolicyURL"], |
| 4150 | + eula_url=organization_details["EulaURL"] |
| 4151 | + ).to_json() |
| 4152 | + |
| 4153 | + return data_controller |
| 4154 | + |
| 4155 | + async def create_or_update_data_controller_details_in_wallet(self) -> StorageRecord: |
| 4156 | + """Create or update data controller details in wallet.""" |
| 4157 | + |
| 4158 | + # Storage instance |
| 4159 | + storage: IndyStorage = await self.context.inject(BaseStorage) |
| 4160 | + |
| 4161 | + result = json.dumps({}) |
| 4162 | + |
| 4163 | + # Retrieve data controller details from storage |
| 4164 | + try: |
| 4165 | + |
| 4166 | + storage_record: StorageRecord = await storage.search_records( |
| 4167 | + self.RECORD_TYPE_DATA_CONTROLLER_DETAILS |
| 4168 | + ).fetch_single() |
| 4169 | + |
| 4170 | + |
| 4171 | + data_controller = await self.fetch_org_details_from_igrantio() |
| 4172 | + |
| 4173 | + await storage.update_record_value(storage_record, data_controller) |
| 4174 | + |
| 4175 | + return data_controller |
| 4176 | + |
| 4177 | + except StorageError as err: |
| 4178 | + |
| 4179 | + try: |
| 4180 | + data_controller = await self.fetch_org_details_from_igrantio() |
| 4181 | + |
| 4182 | + # Create data controller details record |
| 4183 | + storage_record = StorageRecord( |
| 4184 | + self.RECORD_TYPE_DATA_CONTROLLER_DETAILS, |
| 4185 | + data_controller, |
| 4186 | + ) |
| 4187 | + |
| 4188 | + await storage.add_record(storage_record) |
| 4189 | + |
| 4190 | + return data_controller |
| 4191 | + except ADAManagerError as err: |
| 4192 | + # Create data controller details record |
| 4193 | + storage_record = StorageRecord( |
| 4194 | + self.RECORD_TYPE_DATA_CONTROLLER_DETAILS, |
| 4195 | + result, |
| 4196 | + ) |
| 4197 | + |
| 4198 | + |
| 4199 | + await storage.add_record(storage_record) |
| 4200 | + |
| 4201 | + return result |
| 4202 | + |
| 4203 | + except ADAManagerError as err: |
| 4204 | + pass |
| 4205 | + |
| 4206 | + return result |
| 4207 | + |
| 4208 | + async def process_data_controller_details_message(self, data_controller_details_message: DataControllerDetailsMessage, receipt: MessageReceipt) -> None: |
| 4209 | + """ |
| 4210 | + Process data controller details message. |
| 4211 | + """ |
| 4212 | + |
| 4213 | + # Responder instance |
| 4214 | + responder: DispatcherResponder = await self.context.inject(BaseResponder, required=False) |
| 4215 | + |
| 4216 | + # From and To MyData DIDs |
| 4217 | + to_did: DIDMyData = DIDMyData.from_public_key_b58( |
| 4218 | + receipt.sender_verkey, key_type=KeyType.ED25519) |
| 4219 | + from_did: DIDMyData = DIDMyData.from_public_key_b58( |
| 4220 | + receipt.recipient_verkey, key_type=KeyType.ED25519) |
| 4221 | + |
| 4222 | + # Query data_controller_details record |
| 4223 | + data_controller_details: str = await self.create_or_update_data_controller_details_in_wallet() |
| 4224 | + |
| 4225 | + # Construct Data Controller model class |
| 4226 | + data_controller: DataController = DataControllerSchema().load(json.loads(data_controller_details)) |
| 4227 | + |
| 4228 | + # Construct DataControllerDetailsResponseMessage |
| 4229 | + data_controller_details_response_message = DataControllerDetailsResponseMessage( |
| 4230 | + from_did=from_did.did, |
| 4231 | + to_did=to_did.did, |
| 4232 | + created_time=round(time.time() * 1000), |
| 4233 | + body=data_controller |
| 4234 | + ) |
| 4235 | + |
| 4236 | + # Send DataControllerDetailsResponseMessage to the requester. |
| 4237 | + |
| 4238 | + if responder: |
| 4239 | + await responder.send_reply(data_controller_details_response_message, connection_id=self.context.connection_record.connection_id) |
| 4240 | + |
| 4241 | + async def send_data_controller_details_message(self, conn_id: str) -> None: |
| 4242 | + """Send data controller details message.""" |
| 4243 | + |
| 4244 | + # Responder instance |
| 4245 | + responder: DispatcherResponder = await self.context.inject(BaseResponder, required=False) |
| 4246 | + |
| 4247 | + try: |
| 4248 | + |
| 4249 | + # Retrieve connection record by id |
| 4250 | + connection_record: ConnectionRecord = await ConnectionRecord.retrieve_by_id( |
| 4251 | + self.context, |
| 4252 | + conn_id |
| 4253 | + ) |
| 4254 | + |
| 4255 | + except StorageError as err: |
| 4256 | + |
| 4257 | + raise ADAManagerError( |
| 4258 | + f"Failed to retrieve connection record: {err}" |
| 4259 | + ) |
| 4260 | + |
| 4261 | + # From and to mydata dids |
| 4262 | + from_did: DIDMyData = DIDMyData.from_public_key_b58( |
| 4263 | + connection_record.my_did, key_type=KeyType.ED25519 |
| 4264 | + ) |
| 4265 | + to_did: DIDMyData = DIDMyData.from_public_key_b58( |
| 4266 | + connection_record.their_did, key_type=KeyType.ED25519 |
| 4267 | + ) |
| 4268 | + |
| 4269 | + # Construct DataControllerDetailsMessage Message |
| 4270 | + data_controller_details_message = DataControllerDetailsMessage( |
| 4271 | + from_did=from_did.did, |
| 4272 | + to_did=to_did.did, |
| 4273 | + created_time=round(time.time() * 1000) |
| 4274 | + ) |
| 4275 | + |
| 4276 | + # Send message |
| 4277 | + if responder: |
| 4278 | + await responder.send_reply(data_controller_details_message, connection_id=connection_record.connection_id) |
0 commit comments