ЕСОЗ - публічна документація

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Purpose

This WS is used to compete Person request to update the person details according to his id, which was previously found using person_id from access token.

Specification

Apiary

Key points

  1. This WS should be used only for completing person requests for updating existing person in the system.

  2. Only person request created in PIS should be completed by this WS.

  3. Prior completing - scan copies for person documents may be uploaded to media content storage.

Authorization

  • Verify the validity of access token

    • Return (401, 'Invalid access token') in case of validation fails

  • Verify that token is not expired

    • in case of error - return (401, 'Invalid access token')

  • Check user scopes in order to perform this action (scope = 'person_request:write_pis')

    • Return (403, 'Your scope does not allow to access this resource. Missing allowances: person_request:write_pis') in case of invalid scope(s)

  • Check that token contains person_id

    • in case of error - return (401, 'Invalid access token')

Validate person

  • Get person_id from token (x-person-id header)

  • Validate person status is active (status = ‘active' & is_active = 'true’)

    • in case of error - return 404 ('Person is not found')

Validate confidant person and relationship

If person is not legally capable - system must ensure that its details are updated by confidant person and there is registered and verified their relationship

Get applicant_person_id from token, compare it to person_id from token:

  • If equals - check that person must not be authorized by confidant person, so it doesn’t correspond to following rules:

    • persons age < no_self_registration_age global parameter;

    • persons age between no_self_registration_age and person_full_legal_capacity_age global parameters and person does not have document with type from PIS_PERSON_LEGAL_CAPACITY_DOCUMENT_TYPES config parameter;

    • persons age > person_full_legal_capacity_age global parameter and exists at least one active and approved confidant person relationship for person (using following process Check confidant person relationship with person_id = person from request - expected :ok, :approved response)

    • In case of error - return 409 (‘Request must be authorized by confidant person’)

  • If not equal - validate relationship with following steps:

    • Check that there is registered relationship between person_id and applicant_person_id(MPI.confidant_person_relationships)

    • Check that relationship is VERIFIED

      • In case of error - return 409 (‘Can’t confirm relationship’)

    • Check that applicant_person_id exists (status = 'active' & is_active = 'true') and has verification_status any but NOT_VERIFIED

      • In case of error - return 409 (‘Confidant person not found or is not verified’)

Validate request

Validate encoded and decoded request according to JSON Schema

 JSON Schema (encoded)
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "signed_content": {
      "type": "string"
    },
    "signed_content_encoding": {
      "type": "string",
      "enum": [
        "base64"
      ]
    }
  },
  "required": [
    "signed_content",
    "signed_content_encoding"
  ],
  "additionalProperties": false
}
 JSON Schema (decoded)
{
  "$schema": "http://json-schema.org/person_request/schema#",
  "definitions": {
    "phone": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "description": "Dictionary: PHONE_TYPE"
        },
        "number": {
          "type": "string",
          "pattern": "^\\+38[0-9]{10}$"
        }
      },
      "required": [
        "type",
        "number"
      ],
      "additionalProperties": false
    },
    "name": {
      "type": "string",
      "pattern": "^(?!.*[ЫЪЭЁыъэё@%&$^#])[a-zA-ZА-ЯҐЇІЄа-яґїіє0-9№\\\"!\\^\\*)\\]\\[(._-].*$"
    },
    "person_name": {
      "type": "string",
      "pattern": "^(?!.*[ЫЪЭЁыъэё@%&$^#])[А-ЯҐЇІЄа-яґїіє\\'\\-]+(\\s(?!.*[ЫЪЭЁыъэё@%&$^#])[А-ЯҐЇІЄа-яґїіє\\'\\-]+)*$",
      "minLength": 1,
      "maxLength": 255
    },
    "unzr": {
      "type": "string",
      "pattern": "^[0-9]{8}-[0-9]{5}$"
    },
    "tax_id": {
      "type": "string",
      "pattern": "^[0-9]{10}$",
      "minLength": 10,
      "maxLength": 255
    },
    "no_tax_id": {
      "type": "boolean",
      "description": "Status person refused tax_id"
    },
    "gender": {
      "type": "string",
      "description": "Dictionary: GENDER",
      "maxLength": 255
    },
    "channel": {
      "type": "string",
      "enum": [
        "PIS"
      ]
    },
    "status": {
      "type": "string",
      "enum": [
        "NEW"
      ]
    },
    "id": {
      "type": "string"
      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
    },
    "content": {
      "type": "string"
    },
    "address": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "description": "Dictionary: ADDRESS_TYPE"
        },
        "country": {
          "type": "string"
        },
        "area": {
          "$ref": "#/definitions/name"
        },
        "region": {
          "$ref": "#/definitions/name"
        },
        "settlement": {
          "$ref": "#/definitions/name"
        },
        "settlement_type": {
          "type": "string",
          "description": "settlement type Dictionary: SETTLEMENT_TYPE"
        },
        "settlement_id": {
          "type": "string",
          "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
        },
        "street_type": {
          "type": "string",
          "description": "street type Dictionary: STREET_TYPE"
        },
        "street": {
          "$ref": "#/definitions/name"
        },
        "building": {
          "type": "string",
          "pattern": "^[1-9]((?![ЫЪЭЁыъэё])()([А-ЯҐЇІЄа-яґїіє \\/\\'\\-0-9])){0,20}$"
        },
        "apartment": {
          "type": "string"
        },
        "zip": {
          "type": "string",
          "pattern": "^[0-9]{5}$"
        },
        "inserted_by": {
          "type": "string"
        },
        "updated_by": {
          "type": "string"
        },
        "inserted_at": {
          "type": "string"
        },
        "updated_at": {
          "type": "string"
        }
      },
      "required": [
        "type",
        "country",
        "area",
        "settlement",
        "settlement_type",
        "settlement_id",
        "inserted_by",
        "updated_by"
      ],
      "additionalProperties": false
    },
    "series_number_document": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "PASSPORT",
            "COMPLEMENTARY_PROTECTION_CERTIFICATE",
            "REFUGEE_CERTIFICATE",
            "TEMPORARY_CERTIFICATE"
          ],
          "description": "Dictionary: DOCUMENT_TYPE"
        },
        "number": {
          "type": "string",
          "pattern": "^((?![ЫЪЭЁ])([А-ЯҐЇІЄ])){2}[0-9]{6}$"
        },
        "issued_by": {
          "type": "string",
          "minLength": 1
        },
        "issued_at": {
          "type": "string",
          "format": "date"
        }
      },
      "required": [
        "type",
        "number"
      ],
      "additionalProperties": false
    },
    "number_document": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "BIRTH_CERTIFICATE",
            "TEMPORARY_PASSPORT"
          ],
          "description": "Dictionary: DOCUMENT_TYPE"
        },
        "number": {
          "type": "string",
          "pattern": "^(?![ЫЪЭЁыъэё@%&$^#`~:,.*|}{?!])[A-ZА-ЯҐЇІЄ0-9№\\/()-]+$",
          "minLength": 1,
          "maxLength": 255
        },
        "issued_by": {
          "type": "string",
          "minLength": 1
        },
        "issued_at": {
          "type": "string",
          "format": "date"
        }
      },
      "required": [
        "type",
        "number"
      ],
      "additionalProperties": false
    },
    "id_card": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "NATIONAL_ID"
          ],
          "description": "Dictionary: DOCUMENT_TYPE"
        },
        "number": {
          "type": "string",
          "pattern": "^[0-9]{9}$"
        },
        "issued_by": {
          "type": "string",
          "minLength": 1
        },
        "issued_at": {
          "type": "string",
          "format": "date"
        }
      },
      "required": [
        "type",
        "number"
      ],
      "additionalProperties": false
    }
  },
  "type": "object",
  "properties": {
    "person": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
          "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
        },
        "first_name": {
          "type": "string"
        },
        "last_name": {
          "type": "string"
        },
        "second_name": {
          "type": "string"
        },
        "birth_date": {
          "type": "string"
        },
        "birth_country": {
          "type": "string"
        },
        "birth_settlement": {
          "type": "string"
        },
        "gender": {
          "enum": [
            "MALE",
            "FEMALE"
          ]
        },
        "email": {
          "type": "string"
        },
        "no_tax_id": {
          "type": "boolean"
        },
        "tax_id": {
          "type": "string"
        },
        "secret": {
          "type": "string"
        },
        "documents": {
          "type": "array"
        },
        "addresses": {
          "type": "array"
        },
        "phones": {
          "type": "array"
        },
        "unzr": {
          "type": "string"
        },
        "emergency_contact": {
          "type": "object",
          "properties": {
            "first_name": {
              "type": "string"
            },
            "last_name": {
              "type": "string"
            },
            "second_name": {
              "type": "string"
            },
            "phones": {
              "type": "array"
            }
          },
          "required": [
            "first_name",
            "last_name",
            "phones"
          ]
        },
        "preferred_way_communication": {
          "enum": [
            "email",
            "phone"
          ]
        }
      },
      "required": [
        "first_name",
        "last_name",
        "birth_date",
        "birth_country",
        "birth_settlement",
        "gender",
        "secret",
        "documents",
        "addresses",
        "emergency_contact"
      ]
    },
    "patient_signed": {
      "type": "boolean"
    },
    "process_disclosure_data_consent": {
      "type": "boolean"
    }
  },
  "required": [
    "id",
    "person",
    "patient_signed",
    "process_disclosure_data_consent",
    "channel",
    "content",
    "status"
  ]
}
  • in case field value does not match the schema - return 422 with field-specific message

  • in case additional fields exist in request - return 422 ('schema does not allow additional properties')

  • in case required parameter does not exist in request - return 422 ('required property %{property} was not present')

  • in case required amount of parameters does not exist in request - return 422 ('expected a minimum of %{min} items but got %{actual}')

Check that person request from URL exists in IL database, person_requests table, with person_data_id = person_id from token

  • in case of error - return 404 ('Person request not found')

Validate status transition

Only person request in NEW status and PIS can be completed.

  • Check that person request for URL has status = NEW and channel = PiS

    • in case of error - return 409 ('Invalid transition')

Validate signed content

  • Check $.signed_content field is a valid base64 string

    • in case of error - return 422 ('Not a base64 string')

  • Check $.signed_content_encoding field value equals to 'base64'

    • in case of error - return 422 ('value is not allowed in enum')

  • Check that $.signed_content field contains digital signature

    • in case of error - return 400 ('Invalid signature')

  • Check digital signature of $.signed_content is valid

    • in case of error - return 400 with digital signature validation error message

  • Check that decoded signed content equals with previously saved in person_requests table for person request (except for $.patient_signed field)

    • in case of error - return 422 ('Signed content does not match the previously created content')

  • Check that drfo field in digital signature equals to person that completes request, based on applicant_person_id field from token

    • if applicant_person_id = person_id from token - compare with person from request

      • if value equals to tax_id regexp (^[0-9]{10}$), field contains tax_id, use tax_id field from person request to compare;

      • if value equals to national_id number regexp (^[0-9]{9}$), field contains national_id number, use documents.number field with documents.type = 'NATIONAL_ID' to compare;

      • if value contains at least one letter, perform reverse transliteration of field using existing algorithm (described here), then check that value equals to passport number regexp (^((?![ЫЪЭЁ])([А-ЯҐЇІЄ])){2}[0-9]{6}$), in case equals, field contains passport number, use documents.number field with documents.type = 'PASSPORT' to compare;

    • if applicant_person_id != person_id from token - compare with confidant person in MPI database

      • if drfo value equals to tax_id regexp (^[0-9]{10}$), field contains tax_id, compare with persons.tax_id;

      • if drfo value equals to national_id number regexp (^[0-9]{9}$), field contains national_id number, compare with document.number with type = 'NATIONAL_ID'

      • if drfo value contains at least one letter, perform reverse transliteration of field using existing algorithm (described here), then check that value equals to passport number regexp (^((?![ЫЪЭЁ])([А-ЯҐЇІЄ])){2}[0-9]{6}$), in case equals, field contains passport number, compare with documents.number with type 'PASSPORT'

        • in case of error - return 409 ('Unable to authenticate signer.')

Validate patient_signed flag

Person request must be signed only after person has read the print form.

After person have read the print form, patient_signed field must be set to true and passed in $.signed_content.

  • Check that patient_signed field exists in decoded person request

    • in case of error - return 422 ('required property patient_signed was not present')

  • Check that patient_signed = true in decoded person request

    • in case of error - return 422 ('value is not allowed in enum')

Check uploaded documents

Get list of document types that must be uploaded to media content storage in documents field of person request.

  • If list is empty - skip validation

  • If list is not empty - check that documents were uploaded, using Media Content Storage

    • in case of error - return 409 ('Documents <<document_types_to_upload>> is not uploaded') with types of documents that must be uploaded to media content storage

Service logic

Save signed person request in Media content storage

After person request is successfully signed, save signed person request to Media content storage.

Get name for bucket from MEDIA_STORAGE_PERSON_REQUEST_BUCKET config parameter, save person request to /person_requests/person_request_id folder with signed_content filename.

Update person request

Update person request, set values:

  • status = SIGNED

  • updated_by = user_id from token

  • updated_at = now()

  • patient_signed = true

  • person_id = person_id from token

Update pending declaration requests

Search for active declaration requests in IL.declaration_requests table with following search parameters:

  • mpi_id = person_id from token

  • status = NEW or APRROVED

If found - cancel declaration requests, set values:

  • status = CANCELLED

  • status_reason = request_cancelled

  • updated_at = now()

  • updated_by = user_id from token

Update person

Update existing person (from person_id from token) in MPI database.

Set values in following tables based on person request:

  1. persons table

  2. person_phones table

  3. person_addresses table

  4. person_documents table

Submit person on verification

Update existing record in person_verifications table for a person according to logic in sections below. Also, set:

  • updated_at = now()

  • updated_by = user uuid

Manual NHS verification

Submit person on Manual NHS verification according to following logic: https://e-health-ua.atlassian.net/wiki/spaces/EH/pages/589430849/Sign+person+request#Manual-NHS-verification

DRFO registry verification

Submit person on DRFO registry verification according to following logic: https://e-health-ua.atlassian.net/wiki/spaces/EH/pages/589430849/Sign+person+request#DRFO-registry-verification

DRACS death acts registry verification

Submit person on DRACS death acts registry verification according to following logic: https://e-health-ua.atlassian.net/wiki/spaces/EH/pages/589430849/Sign+person+request#DRACS-death-acts-registry-verification

Calculate cumulative verification status

Calculate cumulative person verifiation status according to following logic: https://e-health-ua.atlassian.net/wiki/spaces/EH/pages/589430849/Sign+person+request#Calculate-cumulative-verification-status

Update relationship between confidant person and updated person

In case if at least one of submitted person document types exist in PERSON_LEGAL_CAPACITY_DOCUMENT_TYPES config parameter - deactivate all existing confidant person relationships for person:

  • Deactivate all records in IL.confidant_person_relationship_requests table where person_id = person.id and status = NEW, set values:

    • status = CANCELLED

    • updated_at = now()

    • updated_by = user_id (from token)

  • Deactivate all records in MPI.confidant_person_relationship table where person_id = person.id and is_active = true, set values:

    • is_active = false

    • updated_at = now()

    • updated_by = user_id (from token)

  • For each relationship from previous step - deactivate person authentication methods in https://e-health-ua.atlassian.net/wiki/spaces/EH/pages/724729917/MPI#person_authentication_methods table with type = THIRD_PERSON and value = confidant_person_relationships.confidant_person_id, set values:

    • is_active = false

    • ended_at = now()

    • updated_at = now()

    • updated_by = user_id (from token)

Render a response

Render a response according to specification.

  • No labels