├── .coveragerc ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── sonar.yaml │ └── tests.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .pylintrc ├── CONTRIBUTING.md ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── examples ├── aml │ ├── .env.example │ ├── README.md │ ├── app.py │ └── requirements.txt ├── doc_scan │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── __init__.py │ ├── app.py │ ├── requirements.in │ ├── requirements.txt │ ├── settings.py │ ├── static │ │ ├── images │ │ │ ├── favicon.png │ │ │ └── logo.svg │ │ └── style.css │ └── templates │ │ ├── error.html │ │ ├── index.html │ │ ├── layout │ │ ├── footer.html │ │ └── header.html │ │ ├── partials │ │ ├── check.html │ │ └── task.html │ │ └── success.html ├── yoti_example_django │ ├── .env.example │ ├── Dockerfile │ ├── README.md │ ├── app_settings.py │ ├── docker-compose.yml │ ├── manage.py │ ├── requirements.in │ ├── requirements.txt │ └── yoti_example │ │ ├── __init__.py │ │ ├── settings.py │ │ ├── static │ │ ├── .keep │ │ ├── assets │ │ │ ├── app-store-badge.png │ │ │ ├── app-store-badge@2x.png │ │ │ ├── company-logo.jpg │ │ │ ├── google-play-badge.png │ │ │ ├── google-play-badge@2x.png │ │ │ ├── icons │ │ │ │ ├── address.svg │ │ │ │ ├── calendar.svg │ │ │ │ ├── chevron-down-grey.svg │ │ │ │ ├── document.svg │ │ │ │ ├── email.svg │ │ │ │ ├── gender.svg │ │ │ │ ├── nationality.svg │ │ │ │ ├── phone.svg │ │ │ │ ├── profile.svg │ │ │ │ └── verified.svg │ │ │ ├── logo.png │ │ │ └── logo@2x.png │ │ ├── index.css │ │ └── profile.css │ │ ├── templates │ │ ├── attribute_snippet.html │ │ ├── dynamic-share.html │ │ ├── index.html │ │ └── profile.html │ │ ├── urls.py │ │ ├── views.py │ │ └── wsgi.py └── yoti_example_flask │ ├── .env.example │ ├── Dockerfile │ ├── README.md │ ├── app.py │ ├── docker-compose.yml │ ├── requirements.in │ ├── requirements.txt │ ├── settings.py │ ├── static │ ├── .keep │ ├── assets │ │ ├── app-store-badge.png │ │ ├── app-store-badge@2x.png │ │ ├── company-logo.jpg │ │ ├── google-play-badge.png │ │ ├── google-play-badge@2x.png │ │ ├── icons │ │ │ ├── address.svg │ │ │ ├── calendar.svg │ │ │ ├── chevron-down-grey.svg │ │ │ ├── document.svg │ │ │ ├── email.svg │ │ │ ├── gender.svg │ │ │ ├── nationality.svg │ │ │ ├── phone.svg │ │ │ ├── profile.svg │ │ │ └── verified.svg │ │ ├── logo.png │ │ └── logo@2x.png │ ├── index.css │ └── profile.css │ └── templates │ ├── dynamic-share.html │ ├── index.html │ └── profile.html ├── login_flow.png ├── pytest.ini ├── requirements.in ├── requirements.txt ├── setup.cfg ├── setup.py ├── sonar-project.properties └── yoti_python_sdk ├── __init__.py ├── activity_details.py ├── age_verification.py ├── aml.py ├── anchor.py ├── attribute.py ├── attribute_issuance_details.py ├── attribute_parser.py ├── client.py ├── config.py ├── crypto.py ├── date_parser.py ├── doc_scan ├── __init__.py ├── client.py ├── constants.py ├── endpoint.py ├── exception │ ├── __init__.py │ └── doc_scan_exception.py ├── session │ ├── __init__.py │ ├── create │ │ ├── __init__.py │ │ ├── check │ │ │ ├── __init__.py │ │ │ ├── document_authenticity.py │ │ │ ├── document_comparison.py │ │ │ ├── face_match.py │ │ │ ├── liveness.py │ │ │ └── requested_check.py │ │ ├── filter │ │ │ ├── __init__.py │ │ │ ├── document_filter.py │ │ │ ├── document_restrictions_filter.py │ │ │ ├── orthogonal_restrictions_filter.py │ │ │ ├── required_document.py │ │ │ ├── required_id_document.py │ │ │ └── required_supplementary_document.py │ │ ├── notification_config.py │ │ ├── objective │ │ │ ├── __init__.py │ │ │ ├── objective.py │ │ │ └── proof_of_address_objective.py │ │ ├── sdk_config.py │ │ ├── session_spec.py │ │ └── task │ │ │ ├── __init__.py │ │ │ ├── requested_task.py │ │ │ ├── supplementary_doc_text_extraction.py │ │ │ └── text_extraction.py │ └── retrieve │ │ ├── __init__.py │ │ ├── breakdown_response.py │ │ ├── check_response.py │ │ ├── create_session_result.py │ │ ├── document_fields_response.py │ │ ├── document_id_photo_response.py │ │ ├── face_map_response.py │ │ ├── file_response.py │ │ ├── frame_response.py │ │ ├── generated_check_response.py │ │ ├── generated_media.py │ │ ├── get_session_result.py │ │ ├── id_document_resource_response.py │ │ ├── liveness_resource_response.py │ │ ├── media_response.py │ │ ├── media_value.py │ │ ├── page_response.py │ │ ├── recommendation_response.py │ │ ├── report_response.py │ │ ├── resource_container.py │ │ ├── resource_response.py │ │ ├── supplementary_document_resource_response.py │ │ └── task_response.py └── support │ ├── __init__.py │ └── supported_documents.py ├── document_details.py ├── dynamic_sharing_service ├── __init__.py ├── dynamic_scenario_builder.py ├── extension │ ├── __init__.py │ ├── extension_builder.py │ ├── location_constraint_extension_builder.py │ ├── third_party_attribute_extension.py │ └── transactional_flow_extension_builder.py ├── policy │ ├── __init__.py │ ├── dynamic_policy_builder.py │ ├── source_constraint_builder.py │ ├── wanted_anchor_builder.py │ └── wanted_attribute_builder.py └── share_url.py ├── endpoint.py ├── exceptions.py ├── http.py ├── image.py ├── multivalue.py ├── profile.py ├── protobuf ├── __init__.py ├── attribute_public_api │ ├── Attribute_pb2.py │ ├── Attribute_pb2_grpc.py │ ├── ContentType_pb2.py │ ├── ContentType_pb2_grpc.py │ ├── List_pb2.py │ ├── List_pb2_grpc.py │ ├── Signing_pb2.py │ ├── Signing_pb2_grpc.py │ └── __init__.py ├── common_public_api │ ├── EncryptedData_pb2.py │ ├── EncryptedData_pb2_grpc.py │ ├── SignedTimestamp_pb2.py │ ├── SignedTimestamp_pb2_grpc.py │ └── __init__.py ├── protobuf.py └── share_public_api │ ├── DataEntry_pb2.py │ ├── DataEntry_pb2_grpc.py │ ├── ExtraData_pb2.py │ ├── ExtraData_pb2_grpc.py │ ├── IssuingAttributes_pb2.py │ ├── IssuingAttributes_pb2_grpc.py │ ├── ThirdPartyAttribute_pb2.py │ ├── ThirdPartyAttribute_pb2_grpc.py │ └── __init__.py ├── share ├── __init__.py └── extra_data.py ├── tests ├── __init__.py ├── anchor_fixture_parser.py ├── attribute_fixture_parser.py ├── conftest.py ├── doc_scan │ ├── __init__.py │ ├── conftest.py │ ├── exception │ │ ├── __init__.py │ │ └── test_doc_scan_exception.py │ ├── fixtures │ │ ├── failed_request.txt │ │ ├── retrieve_session_success.txt │ │ ├── session_create_success.txt │ │ └── supported_documents_success.txt │ ├── mocks.py │ ├── session │ │ ├── __init__.py │ │ ├── create │ │ │ ├── __init__.py │ │ │ ├── check │ │ │ │ ├── __init__.py │ │ │ │ ├── test_face_match_check.py │ │ │ │ ├── test_liveness_check.py │ │ │ │ ├── test_requested_document_authenticity_check.py │ │ │ │ └── test_requested_document_comparison_check.py │ │ │ ├── filter │ │ │ │ ├── __init__.py │ │ │ │ ├── test_document_restriction_builder.py │ │ │ │ ├── test_document_restrictions_filter.py │ │ │ │ ├── test_orthogonal_restrictions_filter.py │ │ │ │ ├── test_required_document.py │ │ │ │ ├── test_required_id_document.py │ │ │ │ └── test_required_supplementary_document.py │ │ │ ├── objective │ │ │ │ ├── __init__.py │ │ │ │ └── test_proof_of_address_objective.py │ │ │ ├── task │ │ │ │ ├── __init__.py │ │ │ │ ├── test_supplementary_doc_text_extraction_task.py │ │ │ │ └── test_text_extraction_task.py │ │ │ ├── test_notification_config.py │ │ │ ├── test_sdk_config.py │ │ │ └── test_session_spec.py │ │ └── retrieve │ │ │ ├── __init__.py │ │ │ ├── test_breakdown_response.py │ │ │ ├── test_check_response.py │ │ │ ├── test_create_session_result.py │ │ │ ├── test_document_fields_response.py │ │ │ ├── test_document_id_photo_response.py │ │ │ ├── test_face_map_response.py │ │ │ ├── test_file_response.py │ │ │ ├── test_frame_response.py │ │ │ ├── test_generated_check_response.py │ │ │ ├── test_generated_media.py │ │ │ ├── test_get_session_result.py │ │ │ ├── test_id_document_resource_response.py │ │ │ ├── test_liveness_resource_response.py │ │ │ ├── test_media_response.py │ │ │ ├── test_media_value.py │ │ │ ├── test_page_response.py │ │ │ ├── test_recommendation_response.py │ │ │ ├── test_report_response.py │ │ │ ├── test_resource_container.py │ │ │ ├── test_resource_response.py │ │ │ ├── test_supplementary_document_resource_response.py │ │ │ └── test_task_response.py │ ├── support │ │ ├── __init__.py │ │ └── test_supported_documents.py │ └── test_doc_scan_client.py ├── dynamic_sharing_service │ ├── __init__.py │ ├── extension │ │ ├── __init__.py │ │ ├── test_extension_builder.py │ │ ├── test_location_constraint_extension_builder.py │ │ ├── test_third_party_attribute_extension.py │ │ └── test_transactional_flow_extension_builder.py │ ├── policy │ │ ├── __init__.py │ │ ├── test_dynamic_policy_builder.py │ │ ├── test_source_constraint_builder.py │ │ ├── test_wanted_anchor_builder.py │ │ └── test_wanted_attribute_builder.py │ ├── test_dynamic_scenario_builder.py │ └── test_share_url.py ├── file_helper.py ├── fixtures │ ├── aml_response.txt │ ├── anchor_critical_last.txt │ ├── anchor_driving_license.txt │ ├── anchor_passport.txt │ ├── anchor_yoti_admin.txt │ ├── attribute_document_images.txt │ ├── auth_digest_get.txt │ ├── auth_digest_post.txt │ ├── auth_key.txt │ ├── encrypted_yoti_token.txt │ ├── response.txt │ ├── response_create_docs_scan_session.txt │ ├── response_empty_profile.txt │ ├── response_get_docs_scan_media_image.txt │ ├── response_get_docs_scan_media_json.txt │ ├── response_get_docs_scan_session.txt │ ├── response_missing_profile.txt │ ├── response_null_profile.txt │ ├── response_share_url.txt │ ├── sdk-test.pem │ ├── testthirdpartyattribute.txt │ └── unknown_anchor.txt ├── image_helper.py ├── mocks.py ├── protobuf_attribute.py ├── share │ ├── __init__.py │ ├── fixtures │ │ └── testextradata.txt │ └── test_extra_data.py ├── test_activity_details.py ├── test_age_verification.py ├── test_aml.py ├── test_anchor.py ├── test_attribute.py ├── test_attribute_issuance_details.py ├── test_attribute_parser.py ├── test_client.py ├── test_crypto.py ├── test_date_parser.py ├── test_document_details.py ├── test_http.py ├── test_image.py ├── test_multivalue.py └── test_profile.py ├── utils.py └── version.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | yoti_python_sdk/version.py 4 | yoti_python_sdk/tests/** 5 | yoti_python_sdk/protobuf/**/* 6 | examples/** 7 | 8 | [report] 9 | exclude_lines = 10 | raise NotImplementedError -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: " There's a better way to get help!" 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # 11 | # Wait ✋ 12 | # 13 | # There's a better way to get help! 14 | # 15 | # Send your questions or issues to sdksupport@yoti.com 16 | # 17 | # 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | open-pull-requests-limit: 3 8 | target-branch: development 9 | -------------------------------------------------------------------------------- /.github/workflows/sonar.yaml: -------------------------------------------------------------------------------- 1 | name: Sonar Scan 2 | on: [push, pull_request_target] 3 | 4 | jobs: 5 | sonar: 6 | name: Sonar Scan 7 | runs-on: ubuntu-latest 8 | # always run on push events 9 | # only run on pull_request_target event when pull request pulls from fork repository 10 | if: > 11 | github.event_name == 'push' || 12 | github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 18 | 19 | - uses: actions/setup-python@v2.1.4 20 | with: 21 | python-version: 3.9 22 | 23 | - run: pip install -r requirements.txt 24 | 25 | - run: pip install -e .[dev] 26 | 27 | - run: pytest --cov=yoti_python_sdk yoti_python_sdk/tests --cov-report=xml:coverage-reports/coverage-new.xml 28 | 29 | - run: sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace/@g' coverage-reports/coverage-new.xml 30 | 31 | - uses: sonarsource/sonarcloud-github-action@master 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 35 | 36 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | on: [push, pull_request_target] 3 | 4 | jobs: 5 | test: 6 | name: Test (Python ${{ matrix.python-version }}) 7 | runs-on: ubuntu-latest 8 | # always run on push events 9 | # only run on pull_request_target event when pull request pulls from fork repository 10 | if: > 11 | github.event_name == 'push' || 12 | github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | python-version: [3.7, 3.8, 3.9, "3.10"] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - uses: actions/setup-python@v2.3.1 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | 25 | - run: pip install -U setuptools 26 | 27 | - run: pip install -r requirements.txt 28 | 29 | - run: pip install -e .[dev] 30 | 31 | - run: pytest -v 32 | 33 | examples: 34 | name: Check Examples 35 | runs-on: ubuntu-latest 36 | if: > 37 | github.event_name == 'push' || 38 | github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository 39 | 40 | steps: 41 | - uses: actions/checkout@v2 42 | 43 | - uses: actions/setup-python@v2.3.1 44 | with: 45 | python-version: 3.9 46 | 47 | - run: pip install --upgrade setuptools 48 | 49 | - run: pushd examples/aml && pip install -r requirements.txt && popd 50 | 51 | - run: pushd examples/yoti_example_django && pip install --upgrade pip && pip install -r requirements.txt && popd 52 | 53 | - run: pushd examples/yoti_example_flask && pip install -r requirements.txt && popd 54 | 55 | - run: pushd examples/doc_scan && pip install -r requirements.txt && popd 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.* 45 | coverage-reports 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # IPython Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # dotenv 80 | .env 81 | 82 | # virtualenv 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | 92 | # IDE 93 | .idea/ 94 | *.un~ 95 | .history/ 96 | .vscode 97 | 98 | # examples files 99 | examples/yoti_example_django/yoti_example/static/YotiSelfie.jpg 100 | examples/yoti_example_django/db.sqlite3 101 | examples/yoti_example_flask/static/YotiSelfie.jpg 102 | 103 | #.pem files for examples 104 | examples/yoti_example_django/*.pem 105 | examples/yoti_example_flask/*.pem 106 | 107 | .scannerwork 108 | .venv/ 109 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: protobuf/ 2 | repos: 3 | - repo: https://github.com/ambv/black 4 | rev: 22.3.0 5 | hooks: 6 | - id: black 7 | 8 | - repo: https://github.com/PyCQA/flake8 9 | rev: 4.0.1 10 | hooks: 11 | - id: flake8 12 | args: 13 | - --ignore=E501,W5 -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | ignore=tests,protobuf 3 | disable=C0330 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | 1. Fork the repo, develop and test your code changes. 4 | 2. Ensure commit messages clearly define the changes that have been made. 5 | 3. Create a pull request. 6 | 7 | ## Adding Features 8 | 9 | Any features added must be fully tested and documented, with examples supplied in the pull request. 10 | The feature must support the lowest Python version that the SDK supports (see [the GitHub workflow tests file](./.github/workflows/tests.yaml) for all supported versions). The feature 11 | must not introduce any unnecessary dependencies (although introducing a new third party library 12 | is open for discussion if absolutely required). 13 | 14 | ## Pre-commit Hook 15 | 16 | * Install the [pre-commit framework](https://pre-commit.com/) 17 | * Run `pre-commit install` 18 | 19 | ## Testing 20 | 21 | After cloning the repository, run the following to install dependencies: 22 | 23 | ```bash 24 | pip install -r requirements.txt 25 | pip install -e .[dev] 26 | ``` 27 | 28 | Running the tests: 29 | 30 | ```bash 31 | pytest 32 | ``` 33 | 34 | ## Coding Style 35 | 36 | * The pre-commit hook uses the `black` formatter to auto-format any code when committing, 37 | along with `flake8` for style guide enforcement. -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | Copyright © 2017 Yoti Ltd 3 | 4 | * * * 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | prune yoti_python_sdk/tests 2 | -------------------------------------------------------------------------------- /examples/aml/.env.example: -------------------------------------------------------------------------------- 1 | YOTI_CLIENT_SDK_ID=yourClientSdkId 2 | YOTI_KEY_FILE_PATH=yourKeyFilePath 3 | -------------------------------------------------------------------------------- /examples/aml/README.md: -------------------------------------------------------------------------------- 1 | ### AML Example Project 2 | 3 | 1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values 4 | 1. Install requirements with `pip install -r requirements.txt` 5 | 1. Run: `python app.py` -------------------------------------------------------------------------------- /examples/aml/app.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os import environ 3 | from os.path import join, dirname 4 | 5 | from dotenv import load_dotenv 6 | 7 | from yoti_python_sdk import Client 8 | from yoti_python_sdk import aml 9 | 10 | dotenv_path = join(dirname(__file__), ".env") 11 | load_dotenv(dotenv_path) 12 | 13 | YOTI_CLIENT_SDK_ID = environ.get("YOTI_CLIENT_SDK_ID") 14 | YOTI_KEY_FILE_PATH = environ.get("YOTI_KEY_FILE_PATH") 15 | 16 | 17 | # The following exits cleanly on Ctrl-C, 18 | # while treating other exceptions as before. 19 | def cli_exception(exception_type, value, tb): 20 | if not issubclass(exception_type, KeyboardInterrupt): 21 | sys.__excepthook__(exception_type, value, tb) 22 | 23 | 24 | given_names = "Edward Richard George" 25 | family_name = "Heath" 26 | 27 | aml_address = aml.AmlAddress(country="GBR") 28 | aml_profile = aml.AmlProfile(given_names, family_name, aml_address) 29 | 30 | if sys.stdin.isatty(): 31 | sys.excepthook = cli_exception 32 | 33 | client = Client(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH) 34 | 35 | aml_result = client.perform_aml_check(aml_profile) 36 | print("AML Result for {0} {1}:".format(given_names, family_name)) 37 | print("On PEP list: " + str(aml_result.on_pep_list)) 38 | print("On fraud list: " + str(aml_result.on_fraud_list)) 39 | print("On watchlist: " + str(aml_result.on_watch_list)) 40 | -------------------------------------------------------------------------------- /examples/aml/requirements.txt: -------------------------------------------------------------------------------- 1 | yoti>=2.13.0 2 | python-dotenv>=0.7.1 3 | -------------------------------------------------------------------------------- /examples/doc_scan/.env.example: -------------------------------------------------------------------------------- 1 | # Required Keys 2 | YOTI_CLIENT_SDK_ID=yourClientSdkId 3 | YOTI_KEY_FILE_PATH=yourKeyFilePath 4 | -------------------------------------------------------------------------------- /examples/doc_scan/.gitignore: -------------------------------------------------------------------------------- 1 | *.pem -------------------------------------------------------------------------------- /examples/doc_scan/README.md: -------------------------------------------------------------------------------- 1 | # Doc Scan Example 2 | 3 | ## Running the example 4 | 5 | 1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values 6 | 1. Install the dependencies with `pip install -r requirements.txt` 7 | 1. Start the server `flask run --cert=adhoc` 8 | 1. Visit `https://localhost:5000` 9 | -------------------------------------------------------------------------------- /examples/doc_scan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/doc_scan/__init__.py -------------------------------------------------------------------------------- /examples/doc_scan/requirements.in: -------------------------------------------------------------------------------- 1 | flask>=1.1.2 2 | python-dotenv>=0.13.0 3 | yoti>=2.13.0 4 | filetype>=1.0.7 5 | pyopenssl>=19.1.0 6 | -------------------------------------------------------------------------------- /examples/doc_scan/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile --output-file=requirements.txt requirements.in 6 | # 7 | asn1==2.2.0 # via yoti 8 | certifi==2020.4.5.1 # via requests 9 | cffi==1.14.0 # via cryptography 10 | chardet==3.0.4 # via requests 11 | click==7.1.2 # via flask 12 | cryptography==3.2 # via pyopenssl, yoti 13 | deprecated==1.2.10 # via yoti 14 | filetype==1.0.7 # via -r requirements.in 15 | flask==1.1.2 # via -r requirements.in 16 | future==0.18.2 # via yoti 17 | idna==2.9 # via requests 18 | iso8601==0.1.13 # via yoti 19 | itsdangerous==1.1.0 # via flask 20 | jinja2==2.11.2 # via flask 21 | markupsafe==1.1.1 # via jinja2 22 | protobuf==3.11.3 # via yoti 23 | pycparser==2.20 # via cffi 24 | pyopenssl==19.1.0 # via -r requirements.in, yoti 25 | python-dotenv==0.13.0 # via -r requirements.in 26 | requests==2.23.0 # via yoti 27 | six==1.14.0 # via cryptography, protobuf, pyopenssl 28 | urllib3==1.25.9 # via requests 29 | werkzeug==1.0.1 # via flask 30 | wrapt==1.12.1 # via deprecated 31 | yoti==2.13.0 # via -r requirements.in 32 | 33 | # The following packages are considered to be unsafe in a requirements file: 34 | # setuptools 35 | -------------------------------------------------------------------------------- /examples/doc_scan/settings.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | from os.path import dirname, join 3 | 4 | from dotenv import load_dotenv 5 | 6 | dotenv_path = join(dirname(__file__), ".env") 7 | load_dotenv(dotenv_path) 8 | 9 | YOTI_CLIENT_SDK_ID = environ.get("YOTI_CLIENT_SDK_ID", None) 10 | YOTI_KEY_FILE_PATH = environ.get("YOTI_KEY_FILE_PATH", None) 11 | 12 | if YOTI_CLIENT_SDK_ID is None or YOTI_KEY_FILE_PATH is None: 13 | raise ValueError("YOTI_CLIENT_SDK_ID or YOTI_KEY_FILE_PATH is None") 14 | 15 | YOTI_APP_BASE_URL = environ.get("YOTI_APP_BASE_URL", "https://localhost:5000") 16 | -------------------------------------------------------------------------------- /examples/doc_scan/static/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/doc_scan/static/images/favicon.png -------------------------------------------------------------------------------- /examples/doc_scan/static/images/logo.svg: -------------------------------------------------------------------------------- 1 | yoti_logos_RGB -------------------------------------------------------------------------------- /examples/doc_scan/static/style.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | padding-top: 4.5rem; 4 | } 5 | 6 | table td:first-child { 7 | width: 30%; 8 | } -------------------------------------------------------------------------------- /examples/doc_scan/templates/error.html: -------------------------------------------------------------------------------- 1 | {% include "layout/header.html" %} 2 |
3 |
4 |
5 |

{{ error }}

6 |
7 |
8 |
9 | {% include "layout/footer.html" %} -------------------------------------------------------------------------------- /examples/doc_scan/templates/index.html: -------------------------------------------------------------------------------- 1 | {% include "layout/header.html" %} 2 | 3 | {% include "layout/footer.html" %} 4 | -------------------------------------------------------------------------------- /examples/doc_scan/templates/layout/footer.html: -------------------------------------------------------------------------------- 1 | 4 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/doc_scan/templates/layout/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Yoti Doc Scan 6 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/doc_scan/templates/partials/task.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
ID{{ task.id }}
State 10 | 11 | {{ task.state }} 12 | 13 |
Created{{ task.created }}
Last Updated{{ task.last_updated }}
-------------------------------------------------------------------------------- /examples/yoti_example_django/.env.example: -------------------------------------------------------------------------------- 1 | YOTI_SCENARIO_ID=yourScenarioId 2 | YOTI_CLIENT_SDK_ID=yourClientSdkId 3 | YOTI_KEY_FILE_PATH=yourKeyFilePath -------------------------------------------------------------------------------- /examples/yoti_example_django/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-slim 2 | ARG YOTI_SCENARIO_ID 3 | ARG YOTI_CLIENT_SDK_ID 4 | ARG YOTI_KEY_FILE_PATH 5 | RUN if [ "$YOTI_SCENARIO_ID" = "yourScenarioId" ] ; then echo YOTI_SCENARIO_ID not set; exit 1; else echo YOTI_SCENARIO_ID is $YOTI_SCENARIO_ID ; fi 6 | RUN if [ "$YOTI_CLIENT_SDK_ID" = "yourClientSdkId" ] ; then echo YOTI_CLIENT_SDK_ID not set; exit 1; else echo YOTI_CLIENT_SDK_ID is $YOTI_CLIENT_SDK_ID ; fi 7 | RUN if [ "$YOTI_KEY_FILE_PATH" = "yourKeyFilePath" ] ; then echo YOTI_KEY_FILE_PATH not set; exit 1; else echo YOTI_KEY_FILE_PATH is $YOTI_KEY_FILE_PATH ; fi 8 | ADD . /yoti-sdk 9 | WORKDIR /yoti-sdk/examples/yoti_example_django/ 10 | RUN pip install --no-cache-dir -r /yoti-sdk/requirements.txt && pip install /yoti-sdk 11 | RUN pip install --no-cache-dir -r requirements.txt 12 | ENV YOTI_SCENARIO_ID $YOTI_SCENARIO_ID 13 | ENV YOTI_CLIENT_SDK_ID $YOTI_CLIENT_SDK_ID 14 | ENV YOTI_KEY_FILE_PATH $YOTI_KEY_FILE_PATH 15 | 16 | RUN python manage.py migrate 17 | CMD ["python", "manage.py", "runsslserver", "0.0.0.0:5000"] 18 | -------------------------------------------------------------------------------- /examples/yoti_example_django/README.md: -------------------------------------------------------------------------------- 1 | ### Django Profile Example Project 2 | 3 | 1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values 4 | 1. Install dependencies: `pip install -r requirements.txt` 5 | 1. Apply migrations before the first start by running: `python manage.py migrate` 6 | 1. Run: `python manage.py runsslserver 0.0.0.0:5000` 7 | 1. Navigate to https://localhost:5000 -------------------------------------------------------------------------------- /examples/yoti_example_django/app_settings.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | 3 | YOTI_SCENARIO_ID = environ.get("YOTI_SCENARIO_ID") 4 | YOTI_CLIENT_SDK_ID = environ.get("YOTI_CLIENT_SDK_ID") 5 | YOTI_KEY_FILE_PATH = environ.get("YOTI_KEY_FILE_PATH") 6 | -------------------------------------------------------------------------------- /examples/yoti_example_django/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | services: 3 | web: 4 | build: 5 | context: ../../ 6 | dockerfile: examples/yoti_example_django/Dockerfile 7 | args: 8 | YOTI_SCENARIO_ID: "${YOTI_SCENARIO_ID}" 9 | YOTI_CLIENT_SDK_ID: "${YOTI_CLIENT_SDK_ID}" 10 | YOTI_KEY_FILE_PATH: "${YOTI_KEY_FILE_PATH}" 11 | ports: 12 | - "5000:5000" 13 | -------------------------------------------------------------------------------- /examples/yoti_example_django/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "yoti_example.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /examples/yoti_example_django/requirements.in: -------------------------------------------------------------------------------- 1 | django>=3.0.7 2 | django-sslserver>=0.22.0 3 | python-dotenv>=0.7.1 4 | requests>=2.20.0 5 | urllib3>=1.24.2 6 | yoti>=2.13.0 7 | -------------------------------------------------------------------------------- /examples/yoti_example_django/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile --output-file=requirements.txt requirements.in 6 | # 7 | asgiref==3.4.1 8 | # via django 9 | asn1==2.2.0 10 | # via yoti 11 | certifi==2018.4.16 12 | # via requests 13 | cffi==1.14.0 14 | # via cryptography 15 | chardet==3.0.4 16 | # via requests 17 | cryptography==3.2 18 | # via 19 | # pyopenssl 20 | # yoti 21 | deprecated==1.2.10 22 | # via yoti 23 | django==3.1.12 24 | # via 25 | # -r requirements.in 26 | # django-sslserver 27 | django-sslserver==0.22 28 | # via -r requirements.in 29 | future==0.16.0 30 | # via yoti 31 | idna==2.7 32 | # via requests 33 | iso8601==0.1.13 34 | # via yoti 35 | protobuf==3.6.0 36 | # via yoti 37 | pycparser==2.18 38 | # via cffi 39 | pyopenssl==18.0.0 40 | # via yoti 41 | python-dotenv==0.8.2 42 | # via -r requirements.in 43 | pytz==2018.4 44 | # via django 45 | requests==2.21.0 46 | # via 47 | # -r requirements.in 48 | # yoti 49 | six==1.11.0 50 | # via 51 | # cryptography 52 | # protobuf 53 | # pyopenssl 54 | sqlparse==0.3.0 55 | # via django 56 | urllib3==1.24.2 57 | # via 58 | # -r requirements.in 59 | # requests 60 | wrapt==1.12.1 61 | # via deprecated 62 | yoti==2.13.0 63 | # via -r requirements.in 64 | 65 | # The following packages are considered to be unsafe in a requirements file: 66 | # setuptools 67 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_django/yoti_example/__init__.py -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_django/yoti_example/static/.keep -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/app-store-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_django/yoti_example/static/assets/app-store-badge.png -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/app-store-badge@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_django/yoti_example/static/assets/app-store-badge@2x.png -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/company-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_django/yoti_example/static/assets/company-logo.jpg -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/google-play-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_django/yoti_example/static/assets/google-play-badge.png -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/google-play-badge@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_django/yoti_example/static/assets/google-play-badge@2x.png -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/address.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/chevron-down-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/email.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/gender.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/nationality.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/phone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/icons/verified.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_django/yoti_example/static/assets/logo.png -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/static/assets/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_django/yoti_example/static/assets/logo@2x.png -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/urls.py: -------------------------------------------------------------------------------- 1 | """yoti_example URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.10/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url 17 | from django.contrib import admin 18 | 19 | from .views import IndexView, AuthView, DynamicShareView, SourceConstraintsView 20 | 21 | urlpatterns = [ 22 | url(r"^$", IndexView.as_view(), name="index"), 23 | url(r"^yoti/auth/$", AuthView.as_view(), name="auth"), 24 | url(r"^admin/", admin.site.urls), 25 | url(r"^dynamic-share/$", DynamicShareView.as_view(), name="dynamic-share"), 26 | url( 27 | r"^source-constraint/$", 28 | SourceConstraintsView.as_view(), 29 | name="source-constraints", 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /examples/yoti_example_django/yoti_example/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for yoti_example project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "yoti_example.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/.env.example: -------------------------------------------------------------------------------- 1 | YOTI_SCENARIO_ID=yourScenarioId 2 | YOTI_CLIENT_SDK_ID=yourClientSdkId 3 | YOTI_KEY_FILE_PATH=yourKeyFilePath 4 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6.3 2 | ARG YOTI_SCENARIO_ID 3 | ARG YOTI_CLIENT_SDK_ID 4 | ARG YOTI_KEY_FILE_PATH 5 | RUN if [ "$YOTI_SCENARIO_ID" = "yourScenarioId" ] ; then echo YOTI_SCENARIO_ID not set; exit 1; else echo YOTI_SCENARIO_ID is $YOTI_SCENARIO_ID ; fi 6 | RUN if [ "$YOTI_CLIENT_SDK_ID" = "yourClientSdkId" ] ; then echo YOTI_CLIENT_SDK_ID not set; exit 1; else echo YOTI_CLIENT_SDK_ID is $YOTI_CLIENT_SDK_ID ; fi 7 | RUN if [ "$YOTI_KEY_FILE_PATH" = "yourKeyFilePath" ] ; then echo YOTI_KEY_FILE_PATH not set; exit 1; else echo YOTI_KEY_FILE_PATH is $YOTI_KEY_FILE_PATH ; fi 8 | ADD . /yoti-sdk 9 | WORKDIR /yoti-sdk/examples/yoti_example_flask/ 10 | RUN pip install --no-cache-dir -r /yoti-sdk/requirements.txt && pip install /yoti-sdk 11 | RUN pip install --no-cache-dir -r requirements.txt 12 | CMD ["python", "app.py"] 13 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/README.md: -------------------------------------------------------------------------------- 1 | #### Flask Profile Example Project 2 | 3 | 1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values 4 | 1. Install dependencies: `pip install -r requirements.txt` 5 | 1. Run `python app.py` 6 | 1. Navigate to https://localhost:5000 -------------------------------------------------------------------------------- /examples/yoti_example_flask/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | services: 3 | web: 4 | build: 5 | context: ../../ 6 | dockerfile: examples/yoti_example_flask/Dockerfile 7 | args: 8 | YOTI_SCENARIO_ID: "${YOTI_SCENARIO_ID}" 9 | YOTI_CLIENT_SDK_ID: "${YOTI_CLIENT_SDK_ID}" 10 | YOTI_KEY_FILE_PATH: "${YOTI_KEY_FILE_PATH}" 11 | ports: 12 | - "5000:5000" 13 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/requirements.in: -------------------------------------------------------------------------------- 1 | click>=7 2 | cffi>=1.15.0 3 | flask>=1.0.4 4 | jinja2>=3.0.3 5 | pyopenssl>=19.0.0 6 | python-dotenv>=0.7.1 7 | requests>=2.20.0 8 | urllib3>=1.24.2 9 | yoti>=2.14.0 10 | werkzeug>=1.0.1 11 | six==1.16.0 -------------------------------------------------------------------------------- /examples/yoti_example_flask/requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with python 3.9 3 | # To update, run: 4 | # 5 | # pip-compile --output-file=requirements.txt requirements.in 6 | # 7 | asn1==2.2.0 8 | # via yoti 9 | certifi==2018.4.16 10 | # via requests 11 | cffi==1.15.0 12 | # via 13 | # -r requirements.in 14 | # cryptography 15 | chardet==3.0.4 16 | # via requests 17 | click==8.1.2 18 | # via 19 | # -r requirements.in 20 | # flask 21 | cryptography==3.2 22 | # via 23 | # pyopenssl 24 | # yoti 25 | deprecated==1.2.10 26 | # via yoti 27 | flask==1.1.1 28 | # via -r requirements.in 29 | future==0.16.0 30 | # via yoti 31 | idna==2.7 32 | # via requests 33 | iso8601==0.1.13 34 | # via yoti 35 | itsdangerous==0.24 36 | # via flask 37 | jinja2==3.0.3 38 | # via 39 | # -r requirements.in 40 | # flask 41 | markupsafe==2.0.1 42 | # via jinja2 43 | protobuf==3.6.0 44 | # via yoti 45 | pycparser==2.18 46 | # via cffi 47 | pyopenssl==19.0.0 48 | # via 49 | # -r requirements.in 50 | # yoti 51 | python-dotenv==0.8.2 52 | # via -r requirements.in 53 | pytz==2020.4 54 | # via yoti 55 | requests==2.21.0 56 | # via 57 | # -r requirements.in 58 | # yoti 59 | six==1.16.0 60 | # via 61 | # -r requirements.in 62 | # cryptography 63 | # protobuf 64 | # pyopenssl 65 | urllib3==1.24.2 66 | # via 67 | # -r requirements.in 68 | # requests 69 | werkzeug==1.0.1 70 | # via 71 | # -r requirements.in 72 | # flask 73 | wrapt==1.12.1 74 | # via deprecated 75 | yoti==2.14.0 76 | # via -r requirements.in 77 | 78 | # The following packages are considered to be unsafe in a requirements file: 79 | # setuptools 80 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/settings.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | 3 | YOTI_SCENARIO_ID = environ.get("YOTI_SCENARIO_ID") 4 | YOTI_CLIENT_SDK_ID = environ.get("YOTI_CLIENT_SDK_ID") 5 | YOTI_KEY_FILE_PATH = environ.get("YOTI_KEY_FILE_PATH") 6 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_flask/static/.keep -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/app-store-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_flask/static/assets/app-store-badge.png -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/app-store-badge@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_flask/static/assets/app-store-badge@2x.png -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/company-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_flask/static/assets/company-logo.jpg -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/google-play-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_flask/static/assets/google-play-badge.png -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/google-play-badge@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_flask/static/assets/google-play-badge@2x.png -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/address.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/chevron-down-grey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/email.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/gender.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/nationality.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/phone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/profile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/icons/verified.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_flask/static/assets/logo.png -------------------------------------------------------------------------------- /examples/yoti_example_flask/static/assets/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/examples/yoti_example_flask/static/assets/logo@2x.png -------------------------------------------------------------------------------- /login_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/login_flow.png -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | python_files = test_*.py 3 | norecursedirs = venv -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | asn1==2.2.0 # asn1 2.3.0 introduces enum34 as a dependency, which causes problems on some envs 2 | cryptography==2.8.0 3 | cffi==1.14.3 4 | future==0.18.2 5 | itsdangerous==1.1.0 6 | pbr==1.10.0 7 | protobuf==4.21.12 8 | pyopenssl==19.1.0 9 | PyYAML==5.2 # PyYAML 5.3 does not support Python 3.4 10 | pytz==2022.1 11 | requests>=2.20.0 12 | urllib3>=1.24.3 13 | deprecated==1.2.10 14 | wheel==0.33.6 15 | iso8601==0.1.13 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile with python 3.9 3 | # To update, run: 4 | # 5 | # pip-compile --output-file=requirements.txt requirements.in 6 | # 7 | asn1==2.2.0 8 | # via -r requirements.in 9 | certifi==2018.11.29 10 | # via requests 11 | cffi==1.14.3 12 | # via 13 | # -r requirements.in 14 | # cryptography 15 | chardet==3.0.4 16 | # via requests 17 | cryptography==2.8 18 | # via 19 | # -r requirements.in 20 | # pyopenssl 21 | deprecated==1.2.10 22 | # via -r requirements.in 23 | future==0.18.2 24 | # via -r requirements.in 25 | idna==2.7 26 | # via requests 27 | iso8601==0.1.13 28 | # via -r requirements.in 29 | itsdangerous==1.1.0 30 | # via -r requirements.in 31 | pbr==1.10.0 32 | # via -r requirements.in 33 | protobuf==4.21.12 34 | # via -r requirements.in 35 | pycparser==2.18 36 | # via cffi 37 | pyopenssl==19.1.0 38 | # via -r requirements.in 39 | pytz==2022.1 40 | # via -r requirements.in 41 | pyyaml==5.2 42 | # via -r requirements.in 43 | requests==2.21.0 44 | # via -r requirements.in 45 | six==1.10.0 46 | # via 47 | # cryptography 48 | # protobuf 49 | # pyopenssl 50 | urllib3==1.24.3 51 | # via 52 | # -r requirements.in 53 | # requests 54 | wheel==0.33.6 55 | # via -r requirements.in 56 | wrapt==1.11.2 57 | # via deprecated 58 | 59 | # The following packages are considered to be unsafe in a requirements file: 60 | # setuptools 61 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | 4 | [bdist_wheel] 5 | universal=1 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from setuptools import find_packages 3 | from setuptools import setup 4 | 5 | version = {} 6 | with open("yoti_python_sdk/version.py") as fp: 7 | exec(fp.read(), version) 8 | 9 | setup( 10 | name="yoti", 11 | version=version["__version__"], 12 | packages=find_packages(include=["yoti_python_sdk", "yoti_python_sdk.*"]), 13 | license="MIT", 14 | description="The Yoti Python SDK, providing API support for Login, Verify (2FA) and Age Verification.", 15 | long_description=open("README.md").read(), 16 | long_description_content_type="text/markdown", 17 | url="https://github.com/getyoti/yoti-python-sdk", 18 | author="Yoti", 19 | author_email="websdk@yoti.com", 20 | install_requires=[ 21 | "deprecated==1.2.10", 22 | "cryptography>=2.2.1", 23 | "protobuf==3.20.1", 24 | "requests>=2.11.1", 25 | "future>=0.11.0", 26 | "asn1==2.2.0", 27 | "pyopenssl>=18.0.0", 28 | "iso8601==0.1.13", 29 | "pytz==2022.1", 30 | ], 31 | extras_require={ 32 | "examples": [ 33 | "Django>=3.0.7", 34 | "Flask>=1.0.4", 35 | "python-dotenv>=0.7.1", 36 | "django-sslserver>=0.22.0", 37 | "Werkzeug==1.0.1", 38 | ], 39 | "dev": [ 40 | "pre-commit==1.17.0", 41 | "pytest>=4.6.11", 42 | "pytest-cov>=2.7.1", 43 | "pylint==1.9.4", 44 | "pylint-exit>=1.1.0", 45 | "python-coveralls==2.9.3", 46 | "coverage==4.5.4", 47 | "mock==2.0.0", 48 | "virtualenv==20.1.0", 49 | ], 50 | }, 51 | classifiers=[ 52 | "Development Status :: 5 - Production/Stable", 53 | "License :: OSI Approved :: MIT License", 54 | "Operating System :: OS Independent", 55 | "Intended Audience :: Developers", 56 | "Programming Language :: Python", 57 | "Programming Language :: Python :: 2", 58 | "Programming Language :: Python :: 2.7", 59 | "Programming Language :: Python :: 3", 60 | "Programming Language :: Python :: 3.4", 61 | "Programming Language :: Python :: 3.5", 62 | "Programming Language :: Python :: 3.6", 63 | "Programming Language :: Python :: 3.7", 64 | "Topic :: Software Development :: Libraries :: Python Modules", 65 | ], 66 | keywords="yoti sdk 2FA multifactor authentication verification identity login register verify 2Factor", 67 | ) 68 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.host.url = https://sonarcloud.io 2 | sonar.organization = getyoti 3 | sonar.projectKey = getyoti:python 4 | sonar.projectName = Python SDK 5 | sonar.projectVersion = 2.14.2 6 | sonar.exclusions = yoti_python_sdk/tests/**,examples/**,yoti_python_sdk/protobuf/**/* 7 | 8 | sonar.python.pylint.reportPath = coverage.out 9 | sonar.verbose = true 10 | sonar.coverage.exclusions = yoti_python_sdk/version.py 11 | -------------------------------------------------------------------------------- /yoti_python_sdk/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from distutils.util import convert_path 4 | from os import environ 5 | 6 | from yoti_python_sdk.client import Client 7 | 8 | DEFAULTS = { 9 | "YOTI_API_URL": "https://api.yoti.com", 10 | "YOTI_API_PORT": 443, 11 | "YOTI_API_VERSION": "v1", 12 | "YOTI_API_VERIFY_SSL": "true", 13 | } 14 | 15 | main_ns = {} 16 | 17 | directory_name = os.path.dirname(__file__) 18 | version_path = os.path.join(directory_name, "version.py") 19 | 20 | ver_path = convert_path(version_path) 21 | with open(ver_path) as ver_file: 22 | exec(ver_file.read(), main_ns) 23 | 24 | __version__ = main_ns["__version__"] 25 | YOTI_API_URL = environ.get("YOTI_API_URL", DEFAULTS["YOTI_API_URL"]) 26 | 27 | YOTI_PROFILE_ENDPOINT = "/api/v1" 28 | YOTI_DOC_SCAN_ENDPOINT = "/idverify/v1" 29 | 30 | YOTI_API_PORT = environ.get("YOTI_API_PORT", DEFAULTS["YOTI_API_PORT"]) 31 | YOTI_API_VERSION = environ.get("YOTI_API_VERSION", DEFAULTS["YOTI_API_VERSION"]) 32 | 33 | # Fully formatted API URLs 34 | YOTI_API_ENDPOINT = environ.get( 35 | "YOTI_API_ENDPOINT", 36 | "{0}:{1}{2}".format(YOTI_API_URL, YOTI_API_PORT, YOTI_PROFILE_ENDPOINT), 37 | ) 38 | YOTI_DOC_SCAN_API_URL = environ.get( 39 | "YOTI_DOC_SCAN_API_URL", 40 | "{0}:{1}{2}".format(YOTI_API_URL, YOTI_API_PORT, YOTI_DOC_SCAN_ENDPOINT), 41 | ) 42 | 43 | YOTI_API_VERIFY_SSL = environ.get( 44 | "YOTI_API_VERIFY_SSL", DEFAULTS["YOTI_API_VERIFY_SSL"] 45 | ) 46 | if YOTI_API_VERIFY_SSL.lower() == "false": 47 | YOTI_API_VERIFY_SSL = False 48 | else: 49 | YOTI_API_VERIFY_SSL = True 50 | 51 | __all__ = ["Client", __version__] 52 | -------------------------------------------------------------------------------- /yoti_python_sdk/age_verification.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk.exceptions import MalformedAgeVerificationException 2 | from yoti_python_sdk import config 3 | 4 | 5 | class AgeVerification(object): 6 | def __init__(self, derived_attribute): 7 | self.__derived_attribute = derived_attribute 8 | 9 | split = derived_attribute.name.split(":") 10 | if len(split) != 2: 11 | raise MalformedAgeVerificationException 12 | 13 | if ( 14 | split[0] in config.ATTRIBUTE_AGE_OVER 15 | or split[0] in config.ATTRIBUTE_AGE_UNDER 16 | ): 17 | self.__check_type = split[0] 18 | else: 19 | raise MalformedAgeVerificationException 20 | 21 | try: 22 | self.__age_verified = int(split[1]) 23 | if derived_attribute.value == "true": 24 | self.__result = True 25 | elif derived_attribute.value == "false": 26 | self.__result = False 27 | except Exception: 28 | raise MalformedAgeVerificationException 29 | 30 | @property 31 | def age(self): 32 | return self.__age_verified 33 | 34 | @property 35 | def check_type(self): 36 | return self.__check_type 37 | 38 | @property 39 | def result(self): 40 | return self.__result 41 | 42 | @property 43 | def attribute(self): 44 | return self.__derived_attribute 45 | -------------------------------------------------------------------------------- /yoti_python_sdk/aml.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | class AmlResult: 5 | def __init__(self, response_text): 6 | if not response_text: 7 | raise ValueError("AML Response is not valid") 8 | 9 | try: 10 | self.on_pep_list = json.loads(response_text).get("on_pep_list") 11 | self.on_fraud_list = json.loads(response_text).get("on_fraud_list") 12 | self.on_watch_list = json.loads(response_text).get("on_watch_list") 13 | 14 | except (AttributeError, IOError, TypeError, OSError) as exc: 15 | error = 'Could not parse AML result from response: "{0}"'.format( 16 | response_text 17 | ) 18 | exception = "{0}: {1}".format(type(exc).__name__, exc) 19 | raise RuntimeError("{0}: {1}".format(error, exception)) 20 | 21 | self.__check_for_none_values(self.on_pep_list) 22 | self.__check_for_none_values(self.on_fraud_list) 23 | self.__check_for_none_values(self.on_watch_list) 24 | 25 | @staticmethod 26 | def __check_for_none_values(arg): 27 | if arg is None: 28 | raise TypeError( 29 | str.format( 30 | "{0} argument was unable to be retrieved from the response", arg 31 | ) 32 | ) 33 | 34 | def __iter__(self): 35 | yield "on_pep_list", self.on_pep_list 36 | yield "on_fraud_list", self.on_fraud_list 37 | yield "on_watch_list", self.on_watch_list 38 | 39 | 40 | class AmlAddress: 41 | def __init__(self, country, postcode=None): 42 | self.country = country 43 | self.post_code = postcode 44 | 45 | 46 | class AmlProfile: 47 | def __init__(self, given_names, family_name, address, ssn=None): 48 | self.given_names = given_names 49 | self.family_name = family_name 50 | self.address = address.__dict__ 51 | self.ssn = ssn 52 | -------------------------------------------------------------------------------- /yoti_python_sdk/attribute.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk import config 2 | 3 | 4 | class Attribute: 5 | def __init__(self, name=None, value=None, anchors=None): 6 | if name is None: 7 | name = "" 8 | if value is None: 9 | value = "" 10 | if anchors is None: 11 | anchors = {} 12 | self.__name = name 13 | self.__value = value 14 | self.__anchors = anchors 15 | 16 | @property 17 | def name(self): 18 | return self.__name 19 | 20 | @property 21 | def value(self): 22 | return self.__value 23 | 24 | @property 25 | def anchors(self): 26 | return self.__anchors 27 | 28 | @property 29 | def sources(self): 30 | return list( 31 | filter(lambda a: a.anchor_type == config.ANCHOR_SOURCE, self.__anchors) 32 | ) 33 | 34 | @property 35 | def verifiers(self): 36 | return list( 37 | filter(lambda a: a.anchor_type == config.ANCHOR_VERIFIER, self.__anchors) 38 | ) 39 | -------------------------------------------------------------------------------- /yoti_python_sdk/attribute_issuance_details.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from yoti_python_sdk import date_parser 4 | import base64 5 | 6 | 7 | class Definition(object): 8 | def __init__(self, name): 9 | self.__name = name 10 | 11 | @property 12 | def name(self): 13 | return self.__name 14 | 15 | 16 | class AttributeIssuanceDetails(object): 17 | def __init__(self, data_entry): 18 | self.__token = base64.b64encode(data_entry.issuance_token) 19 | self.__expiry_date = date_parser.datetime_with_microsecond( 20 | data_entry.issuing_attributes.expiry_date 21 | ) 22 | self.__attributes = [ 23 | Definition(a.name) for a in data_entry.issuing_attributes.definitions 24 | ] 25 | 26 | @property 27 | def token(self): 28 | return self.__token 29 | 30 | @property 31 | def attributes(self): 32 | return self.__attributes 33 | 34 | @property 35 | def expiry_date(self): 36 | return self.__expiry_date 37 | -------------------------------------------------------------------------------- /yoti_python_sdk/attribute_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import collections 5 | import json 6 | import logging 7 | 8 | from yoti_python_sdk import multivalue 9 | from yoti_python_sdk.protobuf.protobuf import Protobuf 10 | 11 | 12 | def value_based_on_content_type(value, content_type=None): 13 | from yoti_python_sdk.image import Image 14 | 15 | if content_type == Protobuf.CT_STRING: 16 | return value.decode("utf-8") 17 | elif value == b"": 18 | raise ValueError( 19 | "Content type: '{0}' should not have an empty value".format(content_type) 20 | ) 21 | elif content_type == Protobuf.CT_DATE: 22 | return value.decode("utf-8") 23 | elif content_type in Image.allowed_types(): 24 | return Image(value, content_type) 25 | elif content_type == Protobuf.CT_JSON: 26 | return convert_to_dict(value) 27 | elif content_type == Protobuf.CT_INT: 28 | string_value = value.decode("utf-8") 29 | int_value = int(string_value) 30 | return int_value 31 | elif content_type == Protobuf.CT_MULTI_VALUE: 32 | return tuple(multivalue.parse(value)) 33 | 34 | if logging.getLogger().propagate: 35 | logging.warning( 36 | "Unknown type '{0}', attempting to parse it as a String".format( 37 | content_type 38 | ) 39 | ) 40 | 41 | return value.decode("utf-8") 42 | 43 | 44 | def convert_to_dict(byte_value): 45 | decoder = json.JSONDecoder(object_pairs_hook=collections.OrderedDict, strict=False) 46 | value_to_decode = byte_value.decode() 47 | 48 | return decoder.decode(value_to_decode) 49 | -------------------------------------------------------------------------------- /yoti_python_sdk/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | SDK_IDENTIFIER = "Python" 4 | ATTRIBUTE_AGE_OVER = "age_over:" 5 | ATTRIBUTE_AGE_UNDER = "age_under:" 6 | ATTRIBUTE_DATE_OF_BIRTH = "date_of_birth" 7 | ATTRIBUTE_FAMILY_NAME = "family_name" 8 | ATTRIBUTE_FULL_NAME = "full_name" 9 | ATTRIBUTE_GENDER = "gender" 10 | ATTRIBUTE_GIVEN_NAMES = "given_names" 11 | ATTRIBUTE_NATIONALITY = "nationality" 12 | ATTRIBUTE_EMAIL_ADDRESS = "email_address" 13 | ATTRIBUTE_PHONE_NUMBER = "phone_number" 14 | ATTRIBUTE_POSTAL_ADDRESS = "postal_address" 15 | ATTRIBUTE_SELFIE = "selfie" 16 | ATTRIBUTE_STRUCTURED_POSTAL_ADDRESS = "structured_postal_address" 17 | ATTRIBUTE_DOCUMENT_IMAGES = "document_images" 18 | ATTRIBUTE_DOCUMENT_DETAILS = "document_details" 19 | ATTRIBUTE_APPLICATION_NAME = "application_name" 20 | ATTRIBUTE_APPLICATION_LOGO = "application_logo" 21 | ATTRIBUTE_APPLICATION_URL = "application_url" 22 | ATTRIBUTE_APPLICATION_RECEIPT_BGCOLOR = "application_receipt_bgcolor" 23 | ANCHOR_SOURCE = "SOURCE" 24 | ANCHOR_VERIFIER = "VERIFIER" 25 | KEY_AGE_VERIFIED = "is_age_verified" 26 | KEY_BASE64_SELFIE = "base64_selfie_uri" 27 | KEY_FORMATTED_ADDRESS = "formatted_address" 28 | X_YOTI_AUTH_KEY = "X-Yoti-Auth-Key" 29 | X_YOTI_AUTH_DIGEST = "X-Yoti-Auth-Digest" 30 | X_YOTI_SDK = "X-Yoti-SDK" 31 | X_YOTI_SDK_VERSION = X_YOTI_SDK + "-Version" 32 | JSON_CONTENT_TYPE = "application/json" 33 | 34 | ANCHOR_VALUE_PASSPORT = "PASSPORT" 35 | ANCHOR_VALUE_NATIONAL_ID = "NATIONAL_ID" 36 | ANCHOR_VALUE_PASS_CARD = "PASS_CARD" 37 | ANCHOR_VALUE_DRIVING_LICENCE = "DRIVING_LICENCE" 38 | -------------------------------------------------------------------------------- /yoti_python_sdk/date_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from datetime import datetime, date 4 | import logging 5 | import re 6 | 7 | 8 | ERROR_PARSING_DATE = "Error parsing date" 9 | 10 | 11 | def from_iso_format(string): 12 | parts = [int(a) for a in string.split("-")] 13 | 14 | if len(parts) != 3: 15 | raise ValueError 16 | 17 | return date(parts[0], parts[1], parts[2]) 18 | 19 | 20 | def datetime_with_microsecond(string): 21 | # Python2 does not have a way of parsing date formats. 22 | # Deprecate this once Python2 support is dropped. 23 | time_split = re.split("[^0-9]", string) 24 | parts = len(time_split) 25 | if parts <= 6: 26 | if logging.getLogger().propagate: 27 | logging.warning(ERROR_PARSING_DATE) 28 | return None 29 | 30 | try: 31 | year = int(time_split[0]) 32 | month = int(time_split[1]) 33 | day = int(time_split[2]) 34 | hour = int(time_split[3]) 35 | minute = int(time_split[4]) 36 | second = int(time_split[5]) 37 | microsecond = int(round(float("0." + time_split[6]) * 1e6)) 38 | return datetime(year, month, day, hour, minute, second, microsecond) 39 | except ValueError: 40 | if logging.getLogger().propagate: 41 | logging.warning(ERROR_PARSING_DATE) 42 | return None 43 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/__init__.py: -------------------------------------------------------------------------------- 1 | from .session.create.check.document_authenticity import ( 2 | RequestedDocumentAuthenticityCheckBuilder, 3 | ) 4 | from .session.create.check.document_comparison import ( 5 | RequestedIDDocumentComparisonCheckBuilder, 6 | ) 7 | from .session.create.check.face_match import RequestedFaceMatchCheckBuilder 8 | from .session.create.check.liveness import RequestedLivenessCheckBuilder 9 | from .session.create.task.text_extraction import RequestedTextExtractionTaskBuilder 10 | from .session.create.task.supplementary_doc_text_extraction import ( 11 | RequestedSupplementaryDocTextExtractionTaskBuilder, 12 | ) 13 | from .session.create.notification_config import NotificationConfigBuilder 14 | from .session.create.sdk_config import SdkConfigBuilder 15 | from .session.create.session_spec import SessionSpecBuilder 16 | from .client import DocScanClient 17 | 18 | __all__ = [ 19 | "RequestedDocumentAuthenticityCheckBuilder", 20 | "RequestedLivenessCheckBuilder", 21 | "RequestedFaceMatchCheckBuilder", 22 | "RequestedIDDocumentComparisonCheckBuilder", 23 | "RequestedTextExtractionTaskBuilder", 24 | "RequestedSupplementaryDocTextExtractionTaskBuilder", 25 | "SessionSpecBuilder", 26 | "NotificationConfigBuilder", 27 | "SdkConfigBuilder", 28 | "DocScanClient", 29 | ] 30 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | ID_DOCUMENT_AUTHENTICITY = "ID_DOCUMENT_AUTHENTICITY" 5 | ID_DOCUMENT_COMPARISON = "ID_DOCUMENT_COMPARISON" 6 | ID_DOCUMENT_TEXT_DATA_CHECK = "ID_DOCUMENT_TEXT_DATA_CHECK" 7 | ID_DOCUMENT_TEXT_DATA_EXTRACTION = "ID_DOCUMENT_TEXT_DATA_EXTRACTION" 8 | ID_DOCUMENT_FACE_MATCH = "ID_DOCUMENT_FACE_MATCH" 9 | LIVENESS = "LIVENESS" 10 | ZOOM = "ZOOM" 11 | SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK = "SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK" 12 | SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION = ( 13 | "SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION" 14 | ) 15 | 16 | CAMERA = "CAMERA" 17 | CAMERA_AND_UPLOAD = "CAMERA_AND_UPLOAD" 18 | 19 | RESOURCE_UPDATE = "RESOURCE_UPDATE" 20 | TASK_COMPLETION = "TASK_COMPLETION" 21 | CHECK_COMPLETION = "CHECK_COMPLETION" 22 | SESSION_COMPLETION = "SESSION_COMPLETION" 23 | 24 | ID_DOCUMENT = "ID_DOCUMENT" 25 | SUPPLEMENTARY_DOCUMENT = "SUPPLEMENTARY_DOCUMENT" 26 | ORTHOGONAL_RESTRICTIONS = "ORTHOGONAL_RESTRICTIONS" 27 | DOCUMENT_RESTRICTIONS = "DOCUMENT_RESTRICTIONS" 28 | INCLUSION_WHITELIST = "WHITELIST" 29 | INCLUSION_BLACKLIST = "BLACKLIST" 30 | 31 | ALWAYS = "ALWAYS" 32 | FALLBACK = "FALLBACK" 33 | NEVER = "NEVER" 34 | 35 | DESIRED = "DESIRED" 36 | IGNORE = "IGNORE" 37 | 38 | PROOF_OF_ADDRESS = "PROOF_OF_ADDRESS" 39 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/endpoint.py: -------------------------------------------------------------------------------- 1 | class Endpoint(object): 2 | @staticmethod 3 | def create_docs_session_path(): 4 | return "/sessions" 5 | 6 | @staticmethod 7 | def retrieve_docs_session_path(session_id): 8 | return "/sessions/{sessionId}".format(sessionId=session_id) 9 | 10 | @staticmethod 11 | def delete_docs_session_path(session_id): 12 | return Endpoint.retrieve_docs_session_path(session_id) 13 | 14 | @staticmethod 15 | def get_media_content_path(session_id, media_id): 16 | return "/sessions/{sessionId}/media/{mediaId}/content".format( 17 | sessionId=session_id, mediaId=media_id 18 | ) 19 | 20 | @staticmethod 21 | def delete_media_path(session_id, media_id): 22 | return Endpoint.get_media_content_path(session_id, media_id) 23 | 24 | @staticmethod 25 | def get_supported_documents_path(): 26 | return "/supported-documents" 27 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/exception/__init__.py: -------------------------------------------------------------------------------- 1 | from .doc_scan_exception import DocScanException 2 | 3 | __all__ = ["DocScanException"] 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/doc_scan/session/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/__init__.py: -------------------------------------------------------------------------------- 1 | from .check.document_authenticity import RequestedDocumentAuthenticityCheckBuilder 2 | from .check.face_match import RequestedFaceMatchCheckBuilder 3 | from .check.liveness import RequestedLivenessCheckBuilder 4 | from .notification_config import NotificationConfigBuilder 5 | from .session_spec import SessionSpecBuilder 6 | from .sdk_config import SdkConfigBuilder 7 | 8 | __all__ = [ 9 | "RequestedDocumentAuthenticityCheckBuilder", 10 | "RequestedFaceMatchCheckBuilder", 11 | "RequestedLivenessCheckBuilder", 12 | "NotificationConfigBuilder", 13 | "SessionSpecBuilder", 14 | "SdkConfigBuilder", 15 | ] 16 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/check/__init__.py: -------------------------------------------------------------------------------- 1 | from .document_authenticity import RequestedDocumentAuthenticityCheckBuilder 2 | from .document_comparison import RequestedIDDocumentComparisonCheckBuilder 3 | from .face_match import RequestedFaceMatchCheckBuilder 4 | from .liveness import RequestedLivenessCheckBuilder 5 | 6 | __all__ = [ 7 | "RequestedDocumentAuthenticityCheckBuilder", 8 | "RequestedIDDocumentComparisonCheckBuilder", 9 | "RequestedFaceMatchCheckBuilder", 10 | "RequestedLivenessCheckBuilder", 11 | ] 12 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/check/document_comparison.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.doc_scan.constants import ID_DOCUMENT_COMPARISON 5 | from yoti_python_sdk.utils import YotiSerializable 6 | from .requested_check import RequestedCheck 7 | 8 | 9 | class RequestedIDDocumentComparisonCheckConfig(YotiSerializable): 10 | """ 11 | The configuration applied when creating a Document Comparison Check 12 | """ 13 | 14 | def to_json(self): 15 | return {} 16 | 17 | 18 | class RequestedIDDocumentComparisonCheck(RequestedCheck): 19 | """ 20 | Requests creation of a Document Comparison Check 21 | """ 22 | 23 | def __init__(self, config): 24 | """ 25 | :param config: the requested document Comparison check configuration 26 | :type config: RequestedIDDocumentComparisonCheckConfig 27 | """ 28 | self.__config = config 29 | 30 | @property 31 | def type(self): 32 | return ID_DOCUMENT_COMPARISON 33 | 34 | @property 35 | def config(self): 36 | return self.__config 37 | 38 | 39 | class RequestedIDDocumentComparisonCheckBuilder(object): 40 | """ 41 | Builder to assist creation of :class:`RequestedIDDocumentComparisonCheck` 42 | """ 43 | 44 | @staticmethod 45 | def build(): 46 | config = RequestedIDDocumentComparisonCheckConfig() 47 | return RequestedIDDocumentComparisonCheck(config) 48 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/check/requested_check.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | from abc import abstractmethod 3 | 4 | from yoti_python_sdk.utils import YotiSerializable, remove_null_values 5 | 6 | 7 | class RequestedCheck(YotiSerializable): 8 | """ 9 | Requests creation of a Check to be performed on a document 10 | """ 11 | 12 | __metaclass__ = ABCMeta 13 | 14 | @property 15 | @abstractmethod 16 | def type(self): 17 | """ 18 | Return the type of the Check to create 19 | 20 | :return: the type 21 | :rtype: str 22 | """ 23 | raise NotImplementedError 24 | 25 | @property 26 | @abstractmethod 27 | def config(self): 28 | """ 29 | Return configuration to apply to the Check 30 | 31 | :return: the configuration 32 | """ 33 | raise NotImplementedError 34 | 35 | def to_json(self): 36 | return remove_null_values({"type": self.type, "config": self.config}) 37 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/filter/__init__.py: -------------------------------------------------------------------------------- 1 | from .document_restrictions_filter import ( 2 | DocumentRestrictionBuilder, 3 | DocumentRestrictionsFilterBuilder, 4 | ) 5 | from .orthogonal_restrictions_filter import OrthogonalRestrictionsFilterBuilder 6 | from .required_id_document import RequiredIdDocumentBuilder 7 | from .required_supplementary_document import RequiredSupplementaryDocumentBuilder 8 | 9 | __all__ = [ 10 | "DocumentRestrictionsFilterBuilder", 11 | "DocumentRestrictionBuilder", 12 | "OrthogonalRestrictionsFilterBuilder", 13 | "RequiredIdDocumentBuilder", 14 | "RequiredSupplementaryDocumentBuilder", 15 | ] 16 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/filter/document_filter.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | 3 | from yoti_python_sdk.utils import YotiSerializable, remove_null_values 4 | 5 | 6 | class DocumentFilter(YotiSerializable): 7 | __metaclass__ = ABCMeta 8 | 9 | def __init__(self, filter_type): 10 | self.__filter_type = filter_type 11 | 12 | @property 13 | def type(self): 14 | return self.__filter_type 15 | 16 | def to_json(self): 17 | return remove_null_values({"type": self.type}) 18 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/filter/required_document.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | from abc import abstractmethod 3 | 4 | from yoti_python_sdk.utils import YotiSerializable 5 | 6 | 7 | class RequiredDocument(YotiSerializable): 8 | __metaclass__ = ABCMeta 9 | 10 | @property 11 | @abstractmethod 12 | def type(self): 13 | raise NotImplementedError 14 | 15 | def __new__(cls, *args, **kwargs): 16 | if cls is RequiredDocument: 17 | raise TypeError("RequiredDocument may not be instantiated") 18 | return object.__new__(cls) 19 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/filter/required_id_document.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk.doc_scan.constants import ID_DOCUMENT 2 | from yoti_python_sdk.utils import remove_null_values 3 | from .document_filter import DocumentFilter # noqa: F401 4 | from .required_document import RequiredDocument 5 | 6 | 7 | class RequiredIdDocument(RequiredDocument): 8 | def __init__(self, doc_filter=None): 9 | """ 10 | :param doc_filter: the filter for the document 11 | :type doc_filter: 12 | """ 13 | self.__doc_filter = doc_filter 14 | 15 | @property 16 | def type(self): 17 | return ID_DOCUMENT 18 | 19 | @property 20 | def filter(self): 21 | return self.__doc_filter 22 | 23 | def to_json(self): 24 | return remove_null_values({"type": self.type, "filter": self.__doc_filter}) 25 | 26 | 27 | class RequiredIdDocumentBuilder(object): 28 | """ 29 | Builder used to assist the creation of a required identity document. 30 | 31 | Example:: 32 | 33 | required_id_document = (RequiredIdDocumentBuilder() 34 | .with_filter(some_filter) 35 | .build()) 36 | 37 | """ 38 | 39 | def __init__(self): 40 | self.__id_document_filter = None 41 | 42 | def with_filter(self, id_document_filter): 43 | """ 44 | Sets the filter on the required ID document 45 | 46 | :param id_document_filter: the filter 47 | :type id_document_filter: DocumentFilter 48 | :return: the builder 49 | :rtype: RequiredIdDocumentBuilder 50 | """ 51 | self.__id_document_filter = id_document_filter 52 | return self 53 | 54 | def build(self): 55 | """ 56 | Builds a required ID document, using the values supplied to the builder 57 | 58 | :return: the required ID document 59 | :rtype: RequiredIdDocument 60 | """ 61 | return RequiredIdDocument(self.__id_document_filter) 62 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/objective/__init__.py: -------------------------------------------------------------------------------- 1 | from .proof_of_address_objective import ProofOfAddressObjectiveBuilder 2 | 3 | __all__ = [ 4 | "ProofOfAddressObjectiveBuilder", 5 | ] 6 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/objective/objective.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | from abc import abstractmethod 3 | 4 | from yoti_python_sdk.utils import YotiSerializable, remove_null_values 5 | 6 | 7 | class Objective(YotiSerializable): 8 | """ 9 | The objective of the document 10 | """ 11 | 12 | __metaclass__ = ABCMeta 13 | 14 | @property 15 | @abstractmethod 16 | def type(self): 17 | """ 18 | Return the type of the objective 19 | 20 | :return: the type 21 | :rtype: str 22 | """ 23 | raise NotImplementedError 24 | 25 | def to_json(self): 26 | return remove_null_values({"type": self.type}) 27 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/objective/proof_of_address_objective.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.doc_scan.constants import PROOF_OF_ADDRESS 5 | from .objective import Objective 6 | 7 | 8 | class ProofOfAddressObjective(Objective): 9 | """ 10 | Proof of address document objective 11 | """ 12 | 13 | @property 14 | def type(self): 15 | return PROOF_OF_ADDRESS 16 | 17 | 18 | class ProofOfAddressObjectiveBuilder(object): 19 | """ 20 | Builder to assist creation of :class:`ProofOfAddressObjective` 21 | """ 22 | 23 | def build(self): 24 | """ 25 | :return: the proof of address objective 26 | :rtype: ProofOfAddressObjective 27 | """ 28 | return ProofOfAddressObjective() 29 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/task/__init__.py: -------------------------------------------------------------------------------- 1 | from .text_extraction import RequestedTextExtractionTaskBuilder 2 | from .supplementary_doc_text_extraction import ( 3 | RequestedSupplementaryDocTextExtractionTaskBuilder, 4 | ) 5 | 6 | __all__ = [ 7 | "RequestedTextExtractionTaskBuilder", 8 | "RequestedSupplementaryDocTextExtractionTaskBuilder", 9 | ] 10 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/create/task/requested_task.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta 2 | from abc import abstractmethod 3 | 4 | from yoti_python_sdk.utils import YotiSerializable, remove_null_values 5 | 6 | 7 | class RequestedTask(YotiSerializable): 8 | """ 9 | Requests creation of a Task to be performed on each document 10 | """ 11 | 12 | __metaclass__ = ABCMeta 13 | 14 | @property 15 | @abstractmethod 16 | def type(self): 17 | """ 18 | Returns the type of the Task to create 19 | 20 | :return: the type 21 | :rtype: str 22 | """ 23 | raise NotImplementedError 24 | 25 | @property 26 | @abstractmethod 27 | def config(self): 28 | """ 29 | Configuration to apply to the Task 30 | 31 | :return: the configuration 32 | """ 33 | raise NotImplementedError 34 | 35 | def to_json(self): 36 | return remove_null_values({"type": self.type, "config": self.config}) 37 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/doc_scan/session/retrieve/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/breakdown_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | 5 | class BreakdownResponse(object): 6 | """ 7 | Represents one breakdown item for a given check 8 | """ 9 | 10 | def __init__(self, data): 11 | """ 12 | :param data: the data to parse 13 | :type data: dict 14 | """ 15 | self.__sub_check = data.get("sub_check", None) 16 | self.__result = data.get("result", None) 17 | self.__details = [DetailsResponse(detail) for detail in data.get("details", [])] 18 | 19 | @property 20 | def sub_check(self): 21 | """ 22 | The sub check value for the breakdown 23 | 24 | :return: the sub check value 25 | :rtype: str or None 26 | """ 27 | return self.__sub_check 28 | 29 | @property 30 | def result(self): 31 | """ 32 | The result of the sub check 33 | 34 | :return: the result 35 | :rtype: str or None 36 | """ 37 | return self.__result 38 | 39 | @property 40 | def details(self): 41 | """ 42 | The details of the sub check 43 | 44 | :return: the details 45 | :rtype: list[DetailsResponse] 46 | """ 47 | return self.__details 48 | 49 | 50 | class DetailsResponse(object): 51 | """ 52 | Represents a specific detail for a breakdown 53 | """ 54 | 55 | def __init__(self, data): 56 | self.__name = data.get("name", None) 57 | self.__value = data.get("value", None) 58 | 59 | @property 60 | def name(self): 61 | """ 62 | The name of the details item 63 | 64 | :return: the name 65 | :rtype: str or None 66 | """ 67 | return self.__name 68 | 69 | @property 70 | def value(self): 71 | """ 72 | The value of the details item 73 | 74 | :return: the value 75 | :rtype: str or None 76 | """ 77 | return self.__value 78 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/create_session_result.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | 5 | class CreateSessionResult(object): 6 | """ 7 | The response to a successful CreateSession call 8 | """ 9 | 10 | def __init__(self, data=None): 11 | """ 12 | :param data: the data 13 | :type data: dict or None 14 | """ 15 | if data is None: 16 | data = dict() 17 | 18 | self.__client_session_token_ttl = data.get("client_session_token_ttl", None) 19 | self.__session_id = data.get("session_id", None) 20 | self.__client_session_token = data.get("client_session_token", None) 21 | 22 | @property 23 | def client_session_token_ttl(self): 24 | """ 25 | Returns the time-to-live (TTL) for the client session 26 | token for the created session 27 | 28 | :return: the client session token TTL 29 | :rtype: int or None 30 | """ 31 | return self.__client_session_token_ttl 32 | 33 | @property 34 | def client_session_token(self): 35 | """ 36 | Returns the client session token for the created session 37 | 38 | :return: the client session token 39 | :rtype: str or None 40 | """ 41 | return self.__client_session_token 42 | 43 | @property 44 | def session_id(self): 45 | """ 46 | Session ID of the created session 47 | 48 | :return: the session ID 49 | :rtype: str or None 50 | """ 51 | return self.__session_id 52 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/document_fields_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 5 | 6 | 7 | class DocumentFieldsResponse(object): 8 | """ 9 | Represents the document fields response 10 | """ 11 | 12 | def __init__(self, data=None): 13 | """ 14 | :param data: the data to parse 15 | :type data: dict or None 16 | """ 17 | if data is None: 18 | data = dict() 19 | 20 | if "media" in data.keys(): 21 | self.__media = MediaResponse(data["media"]) 22 | else: 23 | self.__media = None 24 | 25 | @property 26 | def media(self): 27 | """ 28 | The media object for the document fields 29 | 30 | :return: the media 31 | :rtype: MediaResponse or None 32 | """ 33 | return self.__media 34 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/document_id_photo_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 5 | 6 | 7 | class DocumentIdPhotoResponse(object): 8 | """ 9 | Represents the document ID photo response 10 | """ 11 | 12 | def __init__(self, data=None): 13 | """ 14 | :param data: the data to parse 15 | :type data: dict or None 16 | """ 17 | if data is None: 18 | data = dict() 19 | 20 | if "media" in data.keys(): 21 | self.__media = MediaResponse(data["media"]) 22 | else: 23 | self.__media = None 24 | 25 | @property 26 | def media(self): 27 | """ 28 | The media object for the document ID photo 29 | 30 | :return: the media 31 | :rtype: MediaResponse or None 32 | """ 33 | return self.__media 34 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/face_map_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 5 | 6 | 7 | class FaceMapResponse(object): 8 | """ 9 | Represents a FaceMap response object 10 | """ 11 | 12 | def __init__(self, data=None): 13 | """ 14 | :param data: the data to parse 15 | :type data: dict or None 16 | """ 17 | if data is None: 18 | data = dict() 19 | 20 | self.__media = MediaResponse(data["media"]) if "media" in data.keys() else None 21 | 22 | @property 23 | def media(self): 24 | """ 25 | Returns the associated media of the FaceMap 26 | 27 | :return: the media 28 | :rtype: MediaResponse or None 29 | """ 30 | return self.__media 31 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/file_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 5 | 6 | 7 | class FileResponse(object): 8 | """ 9 | Represents a file 10 | """ 11 | 12 | def __init__(self, data=None): 13 | """ 14 | :param data: the data to parse 15 | :type data: dict or None 16 | """ 17 | if data is None: 18 | data = dict() 19 | 20 | self.__media = MediaResponse(data["media"]) if "media" in data.keys() else None 21 | 22 | @property 23 | def media(self): 24 | """ 25 | Returns the media associated with the file 26 | 27 | :return: the media 28 | :rtype: MediaResponse or None 29 | """ 30 | return self.__media 31 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/frame_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 5 | 6 | 7 | class FrameResponse(object): 8 | """ 9 | Represents a frame of a resource 10 | """ 11 | 12 | def __init__(self, data=None): 13 | """ 14 | :param data: the data to parse 15 | :type data: dict or None 16 | """ 17 | if data is None: 18 | data = dict() 19 | 20 | self.__media = MediaResponse(data["media"]) if "media" in data.keys() else None 21 | 22 | @property 23 | def media(self): 24 | """ 25 | Returns the media associated with the frame 26 | 27 | :return: the media 28 | :rtype: MediaResponse or None 29 | """ 30 | return self.__media 31 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/generated_check_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | 5 | class GeneratedCheckResponse(object): 6 | """ 7 | Represents a check response that has been generated by 8 | the session 9 | """ 10 | 11 | def __init__(self, data=None): 12 | """ 13 | :param data: the data to parse 14 | :type data: dict or None 15 | """ 16 | if data is None: 17 | data = dict() 18 | 19 | self.__id = data.get("id", None) 20 | self.__type = data.get("type", None) 21 | 22 | @property 23 | def id(self): 24 | """ 25 | The id of the generated check 26 | 27 | :return: the id 28 | :rtype: str or None 29 | """ 30 | return self.__id 31 | 32 | @property 33 | def type(self): 34 | """ 35 | Returns the type of the generated check 36 | 37 | :return: the type 38 | :rtype: str or None 39 | """ 40 | return self.__type 41 | 42 | 43 | class GeneratedTextDataCheckResponse(GeneratedCheckResponse): 44 | """ 45 | Represents a generated Text Data check response 46 | """ 47 | 48 | pass 49 | 50 | 51 | class GeneratedSupplementaryDocumentTextDataCheckResponse(GeneratedCheckResponse): 52 | """ 53 | Represents a generated Supplementary Document Text Data check response 54 | """ 55 | 56 | pass 57 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/generated_media.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | 5 | class GeneratedMedia(object): 6 | """ 7 | Represents media that has been generated by the session 8 | """ 9 | 10 | def __init__(self, data=None): 11 | """ 12 | :param data: the data to parse 13 | :type data: dict or None 14 | """ 15 | if data is None: 16 | data = dict() 17 | 18 | self.__id = data.get("id", None) 19 | self.__type = data.get("type", None) 20 | 21 | @property 22 | def id(self): 23 | """ 24 | The ID of the generated media 25 | 26 | :return: the ID 27 | :rtype: str or None 28 | """ 29 | return self.__id 30 | 31 | @property 32 | def type(self): 33 | """ 34 | The type of the generated media, e.g. "JSON" 35 | 36 | :return: the type 37 | :rtype: str or None 38 | """ 39 | return self.__type 40 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/liveness_resource_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from .resource_response import ResourceResponse 5 | from .face_map_response import FaceMapResponse 6 | from .frame_response import FrameResponse 7 | 8 | 9 | class LivenessResourceResponse(ResourceResponse): 10 | """ 11 | Represents a Liveness resource for a given session 12 | """ 13 | 14 | def __init__(self, data=None): 15 | if data is None: 16 | data = dict() 17 | 18 | ResourceResponse.__init__(self, data) 19 | 20 | self.__liveness_type = data.get("liveness_type", None) 21 | 22 | @property 23 | def liveness_type(self): 24 | return self.__liveness_type 25 | 26 | 27 | class ZoomLivenessResourceResponse(LivenessResourceResponse): 28 | """ 29 | Represents a Zoom Liveness resource for a given session 30 | """ 31 | 32 | def __init__(self, data=None): 33 | """ 34 | :param data: the data to parse 35 | :type data: dict or None 36 | """ 37 | if data is None: 38 | data = dict() 39 | 40 | LivenessResourceResponse.__init__(self, data) 41 | 42 | self.__facemap = ( 43 | FaceMapResponse(data["facemap"]) if "facemap" in data.keys() else None 44 | ) 45 | self.__frames = [FrameResponse(frame) for frame in data.get("frames", [])] 46 | 47 | @property 48 | def facemap(self): 49 | """ 50 | Returns the associated facemap information for 51 | the zoom liveness resource 52 | 53 | :return: the facemap 54 | :rtype: FaceMapResponse or None 55 | """ 56 | return self.__facemap 57 | 58 | @property 59 | def frames(self): 60 | """ 61 | Returns the list of associated frames for 62 | the zoom liveness resource 63 | 64 | :return: the frames 65 | :rtype: list[FrameResponse] 66 | """ 67 | return self.__frames 68 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/media_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import iso8601 5 | from iso8601 import ParseError 6 | 7 | 8 | class MediaResponse(object): 9 | """ 10 | Represents a media resource 11 | """ 12 | 13 | def __init__(self, data=None): 14 | """ 15 | :param data: the data to parse 16 | :type data: dict or None 17 | """ 18 | if data is None: 19 | data = dict() 20 | 21 | self.__id = data.get("id", None) 22 | self.__type = data.get("type", None) 23 | self.__created = self.__parse_date(data.get("created", None)) 24 | self.__last_updated = self.__parse_date(data.get("last_updated", None)) 25 | 26 | @staticmethod 27 | def __parse_date(date): 28 | if date is None: 29 | return date 30 | 31 | try: 32 | return iso8601.parse_date(date) 33 | except ParseError: 34 | return None 35 | 36 | @property 37 | def id(self): 38 | """ 39 | The ID of the media resource 40 | 41 | :return: the ID 42 | :rtype: str or None 43 | """ 44 | return self.__id 45 | 46 | @property 47 | def type(self): 48 | """ 49 | The type of the media resource, e.g. "JSON" 50 | 51 | :return: the type 52 | :rtype: str or None 53 | """ 54 | return self.__type 55 | 56 | @property 57 | def created(self): 58 | """ 59 | The date the media resource was created 60 | 61 | :return: the created date 62 | :rtype: datetime.datetime or None 63 | """ 64 | return self.__created 65 | 66 | @property 67 | def last_updated(self): 68 | """ 69 | The date the media resource was last updated 70 | 71 | :return: the last updated date 72 | :rtype: datetime.datetime or None 73 | """ 74 | return self.__last_updated 75 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/media_value.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import base64 5 | 6 | 7 | class MediaValue(object): 8 | def __init__(self, content_type, content): 9 | self.__mime_type = content_type 10 | self.__content = content 11 | 12 | @property 13 | def mime_type(self): 14 | return self.__mime_type 15 | 16 | @property 17 | def content(self): 18 | return self.__content 19 | 20 | @property 21 | def base64_content(self): 22 | return "data:%s;base64,%s" % ( 23 | self.mime_type, 24 | base64.b64encode(self.__content).decode("utf-8"), 25 | ) 26 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/page_response.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from .media_response import MediaResponse 5 | from .frame_response import FrameResponse 6 | 7 | 8 | class PageResponse(object): 9 | """ 10 | Represents information about an uploaded document Page 11 | """ 12 | 13 | def __init__(self, data=None): 14 | """ 15 | :param data: the data to parse 16 | :type data: dict or None 17 | """ 18 | if data is None: 19 | data = dict() 20 | 21 | self.__capture_method = ( 22 | data["capture_method"] if "capture_method" in data.keys() else None 23 | ) 24 | self.__media = MediaResponse(data["media"]) if "media" in data.keys() else None 25 | self.__frames = [FrameResponse(frame) for frame in data.get("frames", [])] 26 | 27 | @property 28 | def capture_method(self): 29 | """ 30 | The capture method that was used for the Page 31 | 32 | :return: the capture method 33 | :rtype: str or None 34 | """ 35 | return self.__capture_method 36 | 37 | @property 38 | def media(self): 39 | """ 40 | The media associated with the Page 41 | 42 | :return: the media 43 | :rtype: MediaResponse or None 44 | """ 45 | return self.__media 46 | 47 | @property 48 | def frames(self): 49 | """ 50 | Returns the list of associated frames 51 | 52 | :return: the frames 53 | :rtype: list[FrameResponse] 54 | """ 55 | return self.__frames 56 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/recommendation_response.py: -------------------------------------------------------------------------------- 1 | class RecommendationResponse(object): 2 | """ 3 | Represents the recommendation given for a check 4 | """ 5 | 6 | def __init__(self, data=None): 7 | """ 8 | :param data: the data to parse 9 | :type data: dict or None 10 | """ 11 | if data is None: 12 | data = dict() 13 | 14 | self.__value = data.get("value", None) 15 | self.__reason = data.get("reason", None) 16 | self.__recovery_suggestion = data.get("recovery_suggestion", None) 17 | 18 | @property 19 | def value(self): 20 | """ 21 | Returns the value of the recommendation 22 | 23 | :return: the value 24 | :rtype: str or None 25 | """ 26 | return self.__value 27 | 28 | @property 29 | def reason(self): 30 | """ 31 | Returns the reason of the recommendation 32 | 33 | :return: the reason 34 | :rtype: str or None 35 | """ 36 | return self.__reason 37 | 38 | @property 39 | def recovery_suggestion(self): 40 | """ 41 | Returns the recovery suggestion of the recommendation 42 | 43 | :return: the recovery suggestion 44 | :rtype: str or None 45 | """ 46 | return self.__recovery_suggestion 47 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/report_response.py: -------------------------------------------------------------------------------- 1 | from .breakdown_response import BreakdownResponse 2 | from .recommendation_response import RecommendationResponse 3 | 4 | 5 | class ReportResponse(object): 6 | """ 7 | Represents a report for a given check 8 | """ 9 | 10 | def __init__(self, data=None): 11 | """ 12 | :param data: the data to parse 13 | :type data: dict or None 14 | """ 15 | if data is None: 16 | data = dict() 17 | 18 | self.__recommendation = ( 19 | RecommendationResponse(data["recommendation"]) 20 | if "recommendation" in data.keys() 21 | else None 22 | ) 23 | 24 | self.__breakdown = [ 25 | BreakdownResponse(breakdown) for breakdown in data.get("breakdown", []) 26 | ] 27 | 28 | @property 29 | def recommendation(self): 30 | """ 31 | The recommendation given for a given check/task 32 | 33 | :return: the recommendation 34 | :rtype: RecommendationResponse 35 | """ 36 | return self.__recommendation 37 | 38 | @property 39 | def breakdown(self): 40 | """ 41 | A list of breakdowns for different sub-checks performed 42 | 43 | :return: the list of breakdowns 44 | :rtype: list[BreakdownResponse] 45 | """ 46 | return self.__breakdown 47 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/session/retrieve/resource_response.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk.doc_scan import constants 2 | from .task_response import ( 3 | TaskResponse, 4 | TextExtractionTaskResponse, 5 | SupplementaryDocumentTextExtractionTaskResponse, 6 | ) 7 | 8 | 9 | class ResourceResponse(object): 10 | """ 11 | Represents a resource 12 | """ 13 | 14 | def __init__(self, data=None): 15 | """ 16 | :param data: the data to parse 17 | :type data: dict or None 18 | """ 19 | if data is None: 20 | data = dict() 21 | 22 | self.__id = data.get("id", None) 23 | self.__tasks = [self.__parse_task(task) for task in data.get("tasks", [])] 24 | 25 | @staticmethod 26 | def __parse_task(task): 27 | """ 28 | Return a parsed task from a dictionary 29 | 30 | :param task: the raw task 31 | :type task: dict 32 | :return: the parsed task 33 | :rtype: TaskResponse 34 | """ 35 | types = { 36 | constants.ID_DOCUMENT_TEXT_DATA_EXTRACTION: TextExtractionTaskResponse, 37 | constants.SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION: SupplementaryDocumentTextExtractionTaskResponse, 38 | } 39 | clazz = types.get( 40 | task.get("type", None), TaskResponse # Default fallback for task type 41 | ) 42 | return clazz(task) 43 | 44 | @property 45 | def id(self): 46 | """ 47 | The ID of the resource 48 | 49 | :return: the id 50 | :rtype: str 51 | """ 52 | return self.__id 53 | 54 | @property 55 | def tasks(self): 56 | """ 57 | Tasks associated with a resource 58 | 59 | :return: the list of tasks 60 | :rtype: list[TaskResponse] 61 | """ 62 | return self.__tasks 63 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/support/__init__.py: -------------------------------------------------------------------------------- 1 | from .supported_documents import SupportedDocumentsResponse 2 | 3 | __all__ = ["SupportedDocumentsResponse"] 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/doc_scan/support/supported_documents.py: -------------------------------------------------------------------------------- 1 | class SupportedDocument(object): 2 | def __init__(self, data=None): 3 | if data is None: 4 | data = dict() 5 | 6 | self.__type = data.get("type", None) 7 | 8 | @property 9 | def type(self): 10 | return self.__type 11 | 12 | 13 | class SupportedCountry(object): 14 | def __init__(self, data=None): 15 | if data is None: 16 | data = dict() 17 | 18 | self.__code = data.get("code", None) 19 | self.__supported_documents = [ 20 | SupportedDocument(document) 21 | for document in data.get("supported_documents", []) 22 | ] 23 | 24 | @property 25 | def code(self): 26 | return self.__code 27 | 28 | @property 29 | def supported_documents(self): 30 | return self.__supported_documents 31 | 32 | 33 | class SupportedDocumentsResponse(object): 34 | def __init__(self, data=None): 35 | if data is None: 36 | data = dict() 37 | 38 | self.__supported_countries = [ 39 | SupportedCountry(country) for country in data.get("supported_countries", []) 40 | ] 41 | 42 | @property 43 | def supported_countries(self): 44 | return self.__supported_countries 45 | -------------------------------------------------------------------------------- /yoti_python_sdk/document_details.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from . import date_parser 3 | 4 | 5 | class DocumentDetails(object): 6 | def __init__(self, data): 7 | self.__parse_data(data) 8 | 9 | @property 10 | def document_type(self): 11 | return self.__document_type 12 | 13 | @property 14 | def issuing_country(self): 15 | return self.__issuing_country 16 | 17 | @property 18 | def document_number(self): 19 | return self.__document_number 20 | 21 | @property 22 | def expiration_date(self): 23 | return self.__dict__.get("_DocumentDetails__expiration_date", None) 24 | 25 | @property 26 | def issuing_authority(self): 27 | return self.__dict__.get("_DocumentDetails__issuing_authority", None) 28 | 29 | def __parse_data(self, data): 30 | data = data.split(" ") 31 | if len(data) < 3 or "" in data: 32 | raise ValueError("Invalid value for DocumentDetails") 33 | 34 | self.__document_type = data[0] 35 | self.__issuing_country = data[1] 36 | self.__document_number = data[2] 37 | if len(data) > 3: 38 | date = data[3] 39 | if date != "-": 40 | self.__expiration_date = date_parser.from_iso_format(date) 41 | if len(data) > 4: 42 | self.__issuing_authority = data[4] 43 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .dynamic_scenario_builder import DynamicScenarioBuilder 3 | from .share_url import create_share_url 4 | 5 | __all__ = ["DynamicScenarioBuilder", "create_share_url"] 6 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/dynamic_scenario_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from .policy.dynamic_policy_builder import DynamicPolicyBuilder 5 | 6 | 7 | class DynamicScenarioBuilder(object): 8 | def __init__(self): 9 | self.__scenario = { 10 | "policy": DynamicPolicyBuilder().build(), 11 | "extensions": [], 12 | "callback_endpoint": "", 13 | } 14 | 15 | def with_policy(self, policy): 16 | """ 17 | :param policy: A DynamicPolicy defining the attributes to be requested 18 | """ 19 | self.__scenario["policy"] = policy 20 | return self 21 | 22 | def with_extension(self, extension): 23 | """ 24 | :param extension: An extension to be activated for the scenario 25 | """ 26 | self.__scenario["extensions"].append(extension) 27 | return self 28 | 29 | def with_callback_endpoint(self, callback_endpoint): 30 | """ 31 | :param callback_endpoint: A string with the callback endpoint 32 | """ 33 | self.__scenario["callback_endpoint"] = callback_endpoint 34 | return self 35 | 36 | def build(self): 37 | """ 38 | :returns: Dictionary representation of dynamic scenario 39 | """ 40 | scenario = self.__scenario.copy() 41 | scenario["extensions"] = scenario["extensions"][:] 42 | return scenario 43 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/extension/__init__.py: -------------------------------------------------------------------------------- 1 | from .extension_builder import ExtensionBuilder 2 | from .location_constraint_extension_builder import LocationConstraintExtensionBuilder 3 | from .transactional_flow_extension_builder import TransactionalFlowExtensionBuilder 4 | from .third_party_attribute_extension import ThirdPartyAttributeExtension 5 | 6 | __all__ = [ 7 | "ExtensionBuilder", 8 | "LocationConstraintExtensionBuilder", 9 | "TransactionalFlowExtensionBuilder", 10 | "ThirdPartyAttributeExtension", 11 | ] 12 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/extension/extension_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | 5 | class ExtensionBuilder(object): 6 | def __init__(self): 7 | self.__extension = {} 8 | 9 | def with_extension_type(self, extension_type): 10 | """ 11 | @param extension_type String label for the extension type 12 | """ 13 | self.__extension["type"] = extension_type 14 | return self 15 | 16 | def with_content(self, content): 17 | """ 18 | @param content The extension content 19 | """ 20 | self.__extension["content"] = content 21 | return self 22 | 23 | def build(self): 24 | """ 25 | @return Dictionary representation of an extension 26 | """ 27 | return self.__extension.copy() 28 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/extension/location_constraint_extension_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | 5 | class LocationConstraintExtensionBuilder(object): 6 | LOCATION_CONSTRAINT = "LOCATION_CONSTRAINT" 7 | 8 | def __init__(self): 9 | self.__extension = {} 10 | self.__extension["type"] = self.LOCATION_CONSTRAINT 11 | self.__extension["content"] = {} 12 | self.__device_location = { 13 | "latitude": None, 14 | "longitude": None, 15 | "radius": None, 16 | "max_uncertainty_radius": None, 17 | } 18 | self.__extension["content"]["expected_device_location"] = self.__device_location 19 | 20 | def with_latitude(self, latitude): 21 | if not isinstance(latitude, float) and not isinstance(latitude, int): 22 | raise ValueError("Latitude must be float or int") 23 | if not -90 <= latitude <= 90: 24 | raise ValueError("Latitude must be between -90 and 90 degrees") 25 | self.__device_location["latitude"] = latitude 26 | return self 27 | 28 | def with_longitude(self, longitude): 29 | if not isinstance(longitude, float) and not isinstance(longitude, int): 30 | raise ValueError("Lontitude must be float or int") 31 | if not -180 <= longitude <= 180: 32 | raise ValueError("Longitude must be between -180 and 180 degrees") 33 | self.__device_location["longitude"] = longitude 34 | return self 35 | 36 | def with_radius(self, radius): 37 | if not isinstance(radius, float) and not isinstance(radius, int): 38 | raise ValueError("Radius must be float or int") 39 | if not 0 <= radius: 40 | raise ValueError("Radius must be >= 0") 41 | self.__device_location["radius"] = radius 42 | return self 43 | 44 | def with_uncertainty(self, uncertainty): 45 | if not isinstance(uncertainty, float) and not isinstance(uncertainty, int): 46 | raise ValueError("Uncertainty must be float or int") 47 | if not 0 <= uncertainty: 48 | raise ValueError("Uncertainty must be >= 0") 49 | self.__device_location["max_uncertainty_radius"] = uncertainty 50 | return self 51 | 52 | def build(self): 53 | return self.__extension.copy() 54 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/extension/third_party_attribute_extension.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import copy 5 | 6 | import pytz 7 | 8 | 9 | class ThirdPartyAttributeExtension(object): 10 | THIRDPARTY_ATTRIBUTE = "THIRD_PARTY_ATTRIBUTE" 11 | 12 | def __init__(self): 13 | self.__extension = { 14 | "type": self.THIRDPARTY_ATTRIBUTE, 15 | "content": {"expiry_date": None, "definitions": []}, 16 | } 17 | 18 | def with_expiry_date(self, expiry_date): 19 | """ 20 | :param expiry_date: Expiry date for the attribute. If no timezone info is provided, UTC will be used. 21 | :type expiry_date: datetime 22 | """ 23 | if expiry_date.tzinfo is None: 24 | expiry_date = expiry_date.replace(tzinfo=pytz.UTC) 25 | 26 | utc_time = expiry_date.astimezone(pytz.utc) 27 | rfc_3339_milliseconds = utc_time.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] 28 | self.__extension["content"]["expiry_date"] = rfc_3339_milliseconds + "Z" 29 | return self 30 | 31 | def with_definitions(self, *names): 32 | """ 33 | :param names: attribute definitions 34 | :type names: str or list[str] 35 | """ 36 | self.__extension["content"]["definitions"].extend([{"name": s} for s in names]) 37 | return self 38 | 39 | def build(self): 40 | """ 41 | Builds the object 42 | 43 | :return: the third party attribute 44 | :rtype: ThirdPartyAttributeExtension 45 | """ 46 | return copy.deepcopy(self.__extension) 47 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/extension/transactional_flow_extension_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | 5 | class TransactionalFlowExtensionBuilder(object): 6 | TRANSACTIONAL_FLOW = "TRANSACTIONAL_FLOW" 7 | 8 | def __init__(self): 9 | self.__extension = {} 10 | self.__extension["type"] = self.TRANSACTIONAL_FLOW 11 | 12 | def with_content(self, content): 13 | """ 14 | @param content The extension content 15 | """ 16 | self.__extension["content"] = content 17 | return self 18 | 19 | def build(self): 20 | """ 21 | @return Dictionary representation of an extension 22 | """ 23 | return self.__extension.copy() 24 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/policy/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .wanted_attribute_builder import WantedAttributeBuilder 4 | from .wanted_anchor_builder import WantedAnchorBuilder 5 | from .source_constraint_builder import SourceConstraintBuilder 6 | from .dynamic_policy_builder import DynamicPolicyBuilder 7 | 8 | __all__ = [ 9 | "DynamicPolicyBuilder", 10 | "WantedAttributeBuilder", 11 | "WantedAnchorBuilder", 12 | "SourceConstraintBuilder", 13 | ] 14 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/policy/wanted_anchor_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | 5 | class WantedAnchorBuilder(object): 6 | def __init__(self): 7 | self.__name = "" 8 | self.__subtype = "" 9 | 10 | def with_value(self, name): 11 | """ 12 | :param name: The type of anchor as a string 13 | """ 14 | self.__name = name 15 | return self 16 | 17 | def with_subtype(self, subtype): 18 | """ 19 | :param subtype: Subtype information as a string 20 | """ 21 | self.__subtype = subtype 22 | return self 23 | 24 | def build(self): 25 | """ 26 | :returns: A dict containing the anchor specification 27 | """ 28 | return {"name": self.__name, "sub_type": self.__subtype} 29 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/policy/wanted_attribute_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | 5 | class WantedAttributeBuilder(object): 6 | """ 7 | Builder for WantedAttribute 8 | """ 9 | 10 | def __init__(self): 11 | self.__attribute = {} 12 | self.__constraints = [] 13 | 14 | def with_name(self, name): 15 | """ 16 | :param name: Sets name 17 | """ 18 | self.__attribute["name"] = name 19 | return self 20 | 21 | def with_derivation(self, derivation): 22 | """ 23 | :param derivation: Sets derivation 24 | """ 25 | self.__attribute["derivation"] = derivation 26 | return self 27 | 28 | def with_accept_self_asserted(self, value=True): 29 | """ 30 | :param value: True if self-asserted details are allowed 31 | """ 32 | self.__attribute["accept_self_asserted"] = value 33 | return self 34 | 35 | def with_constraint(self, constraint): 36 | """ 37 | :param constraint: Adds a constraint (e.g. a source constraint) to the 38 | wanted attribute 39 | """ 40 | if isinstance(constraint, list): 41 | self.__constraints.extend(constraint) 42 | else: 43 | self.__constraints.append(constraint) 44 | return self 45 | 46 | def build(self): 47 | """ 48 | :return: The wanted attribute object 49 | """ 50 | if self.__attribute.get("name", None) is None or self.__attribute["name"] == "": 51 | raise ValueError 52 | attribute = self.__attribute.copy() 53 | attribute["constraints"] = self.__constraints[:] 54 | return attribute 55 | -------------------------------------------------------------------------------- /yoti_python_sdk/dynamic_sharing_service/share_url.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import json 5 | from yoti_python_sdk import client 6 | 7 | INVALID_DATA = "Json is incorrect, contains invalid data" 8 | APPLICATION_NOT_FOUND = "Application was not found" 9 | UNKNOWN_ERROR = "Unknown HTTP error occurred" 10 | 11 | 12 | SHARE_URL_ERRORS = { 13 | 400: "Json is incorrect, contains invalid data", 14 | 404: "Application was not found", 15 | } 16 | SHARE_URL_ERRORS.update(client.DEFAULT_HTTP_CLIENT_ERRORS) 17 | 18 | 19 | def create_share_url(yoti_client, dynamic_scenario): 20 | http_method = "POST" 21 | payload = json.dumps(dynamic_scenario, sort_keys=True).encode() 22 | endpoint = yoti_client.endpoints.get_dynamic_share_request_url(no_params=True) 23 | response = yoti_client.make_request(http_method, endpoint, payload) 24 | 25 | client.Client.http_error_handler(response, SHARE_URL_ERRORS) 26 | 27 | response_json = json.loads(response.text) 28 | 29 | return ShareUrl( 30 | _ShareUrl__qr_code=response_json["qrcode"], 31 | _ShareUrl__ref_id=response_json["ref_id"], 32 | ) 33 | 34 | 35 | class ShareUrl(object): 36 | def __init__(self, **kwargs): 37 | self.__dict__.update(kwargs) 38 | 39 | @property 40 | def share_url(self): 41 | return self.__qr_code 42 | 43 | @property 44 | def ref_id(self): 45 | return self.__ref_id 46 | -------------------------------------------------------------------------------- /yoti_python_sdk/endpoint.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk.utils import create_timestamp, create_nonce 2 | 3 | 4 | class Endpoint(object): 5 | def __init__(self, sdk_id): 6 | self.sdk_id = sdk_id 7 | 8 | def get_activity_details_request_path( 9 | self, decrypted_request_token, no_params=False 10 | ): 11 | if no_params: 12 | return "/profile/{0}".format(decrypted_request_token) 13 | 14 | return "/profile/{0}?nonce={1}×tamp={2}&appId={3}".format( 15 | decrypted_request_token, create_nonce(), create_timestamp(), self.sdk_id 16 | ) 17 | 18 | def get_aml_request_url(self, no_params=False): 19 | if no_params: 20 | return "/aml-check" 21 | 22 | return "/aml-check?appId={0}×tamp={1}&nonce={2}".format( 23 | self.sdk_id, create_timestamp(), create_nonce() 24 | ) 25 | 26 | def get_dynamic_share_request_url(self, no_params=False): 27 | if no_params: 28 | return "/qrcodes/apps/{appid}".format(appid=self.sdk_id) 29 | 30 | return "/qrcodes/apps/{appid}?nonce={nonce}×tamp={timestamp}".format( 31 | appid=self.sdk_id, nonce=create_nonce(), timestamp=create_timestamp() 32 | ) 33 | -------------------------------------------------------------------------------- /yoti_python_sdk/exceptions.py: -------------------------------------------------------------------------------- 1 | class MalformedAgeVerificationException(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /yoti_python_sdk/image.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from cryptography.fernet import base64 3 | 4 | from yoti_python_sdk.protobuf.protobuf import Protobuf 5 | 6 | CONTENT_TYPE_JPEG = "jpeg" 7 | CONTENT_TYPE_PNG = "png" 8 | 9 | 10 | class Image: 11 | def __init__(self, image_bytes, image_content_type): 12 | if image_content_type == Protobuf.CT_JPEG: 13 | self.__content_type = CONTENT_TYPE_JPEG 14 | elif image_content_type == Protobuf.CT_PNG: 15 | self.__content_type = CONTENT_TYPE_PNG 16 | else: 17 | raise TypeError( 18 | "Content type '{0}' is not a supported image type".format( 19 | image_content_type 20 | ) 21 | ) 22 | 23 | self.__data = image_bytes 24 | 25 | @staticmethod 26 | def allowed_types(): 27 | return [Protobuf.CT_PNG, Protobuf.CT_JPEG] 28 | 29 | @property 30 | def data(self): 31 | return self.__data 32 | 33 | @property 34 | def content_type(self): 35 | return self.__content_type 36 | 37 | def mime_type(self): 38 | return "image/{0}".format(self.__content_type) 39 | 40 | def base64_content(self): 41 | data = base64.b64encode(self.__data).decode("utf-8") 42 | return "data:{0};base64,{1}".format(self.mime_type(), data) 43 | -------------------------------------------------------------------------------- /yoti_python_sdk/multivalue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from yoti_python_sdk.protobuf import protobuf 3 | 4 | 5 | def parse(multi_value_bytes): 6 | from yoti_python_sdk import ( 7 | attribute_parser, 8 | ) # needed here (and not above) for Python 2.7 & 3.4 dependency handling 9 | 10 | proto = protobuf.Protobuf() 11 | multi_value_list = [] 12 | parsed_multi_value = proto.multi_value(multi_value_bytes) 13 | 14 | for multi_value_item in parsed_multi_value.values: 15 | multi_value_list.append( 16 | attribute_parser.value_based_on_content_type( 17 | multi_value_item.data, multi_value_item.content_type 18 | ) 19 | ) 20 | 21 | return multi_value_list 22 | 23 | 24 | def filter_values(values, type_to_filter): 25 | return tuple(filter(lambda v: isinstance(v, type_to_filter), values)) 26 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/protobuf/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/attribute_public_api/Attribute_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/attribute_public_api/ContentType_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/attribute_public_api/List_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/attribute_public_api/Signing_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/attribute_public_api/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | # needed so we can import relative modules from distinct protobuf-generated files 5 | sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) 6 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/common_public_api/EncryptedData_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/common_public_api/SignedTimestamp_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/common_public_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/protobuf/common_public_api/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/share_public_api/DataEntry_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/share_public_api/ExtraData_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/share_public_api/IssuingAttributes_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/share_public_api/ThirdPartyAttribute_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | import grpc 3 | 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/protobuf/share_public_api/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | # needed so we can import relative modules from distinct protobuf-generated files 5 | sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) -------------------------------------------------------------------------------- /yoti_python_sdk/share/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/share/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/share/extra_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from yoti_python_sdk.attribute_issuance_details import AttributeIssuanceDetails 4 | from yoti_python_sdk.protobuf.share_public_api import ThirdPartyAttribute_pb2 5 | from yoti_python_sdk.protobuf.share_public_api import ExtraData_pb2 6 | 7 | 8 | class ExtraData(object): 9 | THIRD_PARTY_ATTRIBUTE = 6 10 | 11 | def __init__(self, raw): 12 | self.__attribute_issuance_details = None 13 | proto = ExtraData_pb2.ExtraData() 14 | proto.MergeFromString(raw) 15 | data_entries_list = proto.list 16 | 17 | for data_entry in data_entries_list: 18 | if ( 19 | data_entry.type == self.THIRD_PARTY_ATTRIBUTE 20 | and self.__attribute_issuance_details is None 21 | ): 22 | attribute = ThirdPartyAttribute_pb2.ThirdPartyAttribute() 23 | attribute.MergeFromString(data_entry.value) 24 | self.__attribute_issuance_details = AttributeIssuanceDetails(attribute) 25 | 26 | @property 27 | def attribute_issuance_details(self): 28 | return self.__attribute_issuance_details 29 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/anchor_fixture_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from os.path import abspath, dirname, join 3 | 4 | from yoti_python_sdk import anchor 5 | from yoti_python_sdk.protobuf import protobuf 6 | from yoti_python_sdk.tests import file_helper 7 | 8 | FIXTURES_DIR = join(dirname(abspath(__file__)), "fixtures") 9 | ANCHOR_DRIVING_LICENSE = join(FIXTURES_DIR, "anchor_driving_license.txt") 10 | ANCHOR_PASSPORT = join(FIXTURES_DIR, "anchor_passport.txt") 11 | ANCHOR_YOTI_ADMIN = join(FIXTURES_DIR, "anchor_yoti_admin.txt") 12 | ANCHOR_UNKNOWN_ANCHOR = join(FIXTURES_DIR, "unknown_anchor.txt") 13 | ANCHOR_CRITICAL_LAST = join(FIXTURES_DIR, "anchor_critical_last.txt") 14 | 15 | 16 | def get_anchor_from_base64_text(file_path): 17 | anchor_bytes = file_helper.get_file_bytes(file_path) 18 | 19 | protobuf_anchor = protobuf.Protobuf().anchor(anchor_bytes) 20 | anchors = list() 21 | anchors.append(protobuf_anchor) 22 | 23 | return anchors 24 | 25 | 26 | def get_parsed_driving_license_anchor(): 27 | return anchor.Anchor().parse_anchors( 28 | get_anchor_from_base64_text(ANCHOR_DRIVING_LICENSE) 29 | )[0] 30 | 31 | 32 | def get_parsed_anchor_critical_last(): 33 | return anchor.Anchor().parse_anchors( 34 | get_anchor_from_base64_text(ANCHOR_CRITICAL_LAST) 35 | ) 36 | 37 | 38 | def get_parsed_passport_anchor(): 39 | return anchor.Anchor().parse_anchors(get_anchor_from_base64_text(ANCHOR_PASSPORT))[ 40 | 0 41 | ] 42 | 43 | 44 | def get_parsed_yoti_admin_anchor(): 45 | return anchor.Anchor().parse_anchors( 46 | get_anchor_from_base64_text(ANCHOR_YOTI_ADMIN) 47 | )[0] 48 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/attribute_fixture_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from os.path import abspath, dirname, join 3 | 4 | from yoti_python_sdk import attribute_parser 5 | from yoti_python_sdk.protobuf import protobuf 6 | from yoti_python_sdk.tests import file_helper 7 | 8 | FIXTURES_DIR = join(dirname(abspath(__file__)), "fixtures") 9 | ATTRIBUTE_DOCUMENT_IMAGES = join(FIXTURES_DIR, "attribute_document_images.txt") 10 | 11 | 12 | def get_attribute_from_base64_text(file_path): 13 | attribute_bytes = file_helper.get_file_bytes(file_path) 14 | 15 | return protobuf.Protobuf().attribute(attribute_bytes) 16 | 17 | 18 | def parse_multi_value(): 19 | multi_value_proto_attribute = get_attribute_from_base64_text( 20 | ATTRIBUTE_DOCUMENT_IMAGES 21 | ) 22 | 23 | return attribute_parser.value_based_on_content_type( 24 | multi_value_proto_attribute.value, multi_value_proto_attribute.content_type 25 | ) 26 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/conftest.py: -------------------------------------------------------------------------------- 1 | from os.path import abspath 2 | from os.path import dirname 3 | from os.path import join 4 | 5 | import pytest 6 | 7 | from yoti_python_sdk.doc_scan.client import DocScanClient 8 | 9 | FIXTURES_DIR = join(dirname(abspath(__file__)), "..", "fixtures") 10 | PEM_FILE_PATH = join(FIXTURES_DIR, "sdk-test.pem") 11 | 12 | YOTI_CLIENT_SDK_ID = "737204aa-d54e-49a4-8bde-26ddbe6d880c" 13 | 14 | 15 | @pytest.fixture(scope="module") 16 | def doc_scan_client(): 17 | """ 18 | :rtype: DocScanClient 19 | """ 20 | return DocScanClient(YOTI_CLIENT_SDK_ID, PEM_FILE_PATH) 21 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/exception/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/exception/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/fixtures/failed_request.txt: -------------------------------------------------------------------------------- 1 | {"error": "MALFORMED_REQUEST"} -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/fixtures/retrieve_session_success.txt: -------------------------------------------------------------------------------- 1 | {"session_id":"someSessionId"} -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/fixtures/session_create_success.txt: -------------------------------------------------------------------------------- 1 | {"session_id": "someSessionId", "client_session_token": "someClientSessionToken", "client_session_token_ttl": 299} -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/fixtures/supported_documents_success.txt: -------------------------------------------------------------------------------- 1 | {"supported_countries":[{"code":"GBR"},{"code":"USA"}]} -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/mocks.py: -------------------------------------------------------------------------------- 1 | from os.path import abspath 2 | from os.path import dirname 3 | from os.path import join 4 | 5 | from yoti_python_sdk.tests.mocks import MockResponse 6 | 7 | FIXTURES_DIR = join(dirname(abspath(__file__)), "fixtures") 8 | 9 | 10 | def mocked_request_successful_session_creation(): 11 | with open(FIXTURES_DIR + "/session_create_success.txt", "r") as f: 12 | response = f.read() 13 | return MockResponse(status_code=201, text=response) 14 | 15 | 16 | def mocked_request_failed_session_creation(): 17 | with open(FIXTURES_DIR + "/failed_request.txt", "r") as f: 18 | response = f.read() 19 | return MockResponse(status_code=400, text=response) 20 | 21 | 22 | def mocked_request_successful_session_retrieval(): 23 | with open(FIXTURES_DIR + "/retrieve_session_success.txt", "r") as f: 24 | response = f.read() 25 | return MockResponse(status_code=200, text=response) 26 | 27 | 28 | def mocked_request_failed_session_retrieval(): 29 | return MockResponse(status_code=400, text="") 30 | 31 | 32 | def mocked_request_media_content(): 33 | return MockResponse( 34 | status_code=200, 35 | text="someContent", 36 | content=b"someContent", 37 | headers={"Content-Type": "application/json"}, 38 | ) 39 | 40 | 41 | def mocked_request_no_content(): 42 | return MockResponse(status_code=204, text="") 43 | 44 | 45 | def mocked_request_not_found(): 46 | return MockResponse(status_code=404, text="") 47 | 48 | 49 | def mocked_supported_documents_content(): 50 | with open(FIXTURES_DIR + "/supported_documents_success.txt", "r") as f: 51 | response = f.read() 52 | return MockResponse(status_code=200, text=response) 53 | 54 | 55 | def mocked_request_server_error(): 56 | return MockResponse(status_code=500, text="") 57 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/session/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/session/create/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/check/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/session/create/check/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/check/test_face_match_check.py: -------------------------------------------------------------------------------- 1 | import json 2 | import unittest 3 | 4 | from yoti_python_sdk.doc_scan.session.create import RequestedFaceMatchCheckBuilder 5 | from yoti_python_sdk.doc_scan.session.create.check.face_match import ( 6 | RequestedFaceMatchCheck, 7 | ) 8 | from yoti_python_sdk.doc_scan.session.create.check.face_match import ( 9 | RequestedFaceMatchCheckConfig, 10 | ) 11 | from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck 12 | from yoti_python_sdk.utils import YotiEncoder 13 | 14 | 15 | class RequestedFaceMatchCheckTest(unittest.TestCase): 16 | def test_should_build_with_manual_check_always(self): 17 | result = RequestedFaceMatchCheckBuilder().with_manual_check_always().build() 18 | 19 | assert isinstance(result, RequestedCheck) 20 | assert isinstance(result, RequestedFaceMatchCheck) 21 | assert isinstance(result.config, RequestedFaceMatchCheckConfig) 22 | 23 | assert result.type == "ID_DOCUMENT_FACE_MATCH" 24 | assert result.config.manual_check == "ALWAYS" 25 | 26 | def test_should_build_with_manual_check_fallback(self): 27 | result = RequestedFaceMatchCheckBuilder().with_manual_check_fallback().build() 28 | 29 | assert result.config.manual_check == "FALLBACK" 30 | 31 | def test_should_build_with_manual_check_never(self): 32 | result = RequestedFaceMatchCheckBuilder().with_manual_check_never().build() 33 | 34 | assert result.config.manual_check == "NEVER" 35 | 36 | def test_should_serialize_to_json_without_error(self): 37 | result = RequestedFaceMatchCheckBuilder().with_manual_check_never().build() 38 | 39 | s = json.dumps(result, cls=YotiEncoder) 40 | assert s is not None and s != "" 41 | 42 | 43 | if __name__ == "__main__": 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/check/test_liveness_check.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import json 3 | 4 | from yoti_python_sdk.doc_scan.session.create.check import RequestedLivenessCheckBuilder 5 | from yoti_python_sdk.doc_scan.session.create.check.liveness import ( 6 | RequestedLivenessCheck, 7 | ) 8 | from yoti_python_sdk.doc_scan.session.create.check.liveness import ( 9 | RequestedLivenessCheckConfig, 10 | ) 11 | from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck 12 | from yoti_python_sdk.utils import YotiEncoder 13 | 14 | 15 | class RequestedLivenessCheckTest(unittest.TestCase): 16 | def test_should_build_correctly(self): 17 | result = ( 18 | RequestedLivenessCheckBuilder() 19 | .with_liveness_type("SOME_LIVENESS_TYPE") 20 | .with_max_retries(3) 21 | .build() 22 | ) 23 | 24 | assert isinstance(result, RequestedCheck) 25 | assert isinstance(result, RequestedLivenessCheck) 26 | assert isinstance(result.config, RequestedLivenessCheckConfig) 27 | 28 | assert result.type == "LIVENESS" 29 | assert result.config.liveness_type == "SOME_LIVENESS_TYPE" 30 | assert result.config.max_retries == 3 31 | 32 | def test_should_build_with_zoom_liveness_type(self): 33 | result = ( 34 | RequestedLivenessCheckBuilder() 35 | .for_zoom_liveness() 36 | .with_max_retries(5) 37 | .build() 38 | ) 39 | 40 | assert result.type == "LIVENESS" 41 | assert result.config.liveness_type == "ZOOM" 42 | assert result.config.max_retries == 5 43 | 44 | def test_should_serialize_to_json_without_error(self): 45 | result = ( 46 | RequestedLivenessCheckBuilder() 47 | .for_zoom_liveness() 48 | .with_max_retries(5) 49 | .build() 50 | ) 51 | 52 | s = json.dumps(result, cls=YotiEncoder) 53 | assert s is not None and s != "" 54 | 55 | 56 | if __name__ == "__main__": 57 | unittest.main() 58 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/check/test_requested_document_authenticity_check.py: -------------------------------------------------------------------------------- 1 | import json 2 | import unittest 3 | 4 | from yoti_python_sdk.doc_scan.session.create.check import ( 5 | RequestedDocumentAuthenticityCheckBuilder, 6 | ) 7 | from yoti_python_sdk.doc_scan.session.create.check.document_authenticity import ( 8 | RequestedDocumentAuthenticityCheck, 9 | ) 10 | from yoti_python_sdk.doc_scan.session.create.check.document_authenticity import ( 11 | RequestedDocumentAuthenticityCheckConfig, 12 | ) 13 | from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck 14 | from yoti_python_sdk.utils import YotiEncoder 15 | 16 | 17 | class RequestedDocumentAuthenticityCheckTest(unittest.TestCase): 18 | def test_should_build_correctly(self): 19 | result = RequestedDocumentAuthenticityCheckBuilder().build() 20 | 21 | assert isinstance(result, RequestedCheck) 22 | assert isinstance(result, RequestedDocumentAuthenticityCheck) 23 | assert isinstance(result.config, RequestedDocumentAuthenticityCheckConfig) 24 | assert result.type == "ID_DOCUMENT_AUTHENTICITY" 25 | 26 | def test_should_serialize_to_json_without_error(self): 27 | result = RequestedDocumentAuthenticityCheckBuilder().build() 28 | 29 | s = json.dumps(result, cls=YotiEncoder) 30 | assert s is not None and s != "" 31 | 32 | def test_should_build_with_manual_check_always(self): 33 | result = ( 34 | RequestedDocumentAuthenticityCheckBuilder() 35 | .with_manual_check_always() 36 | .build() 37 | ) 38 | 39 | assert result.config.manual_check == "ALWAYS" 40 | 41 | def test_should_build_with_manual_check_fallback(self): 42 | result = ( 43 | RequestedDocumentAuthenticityCheckBuilder() 44 | .with_manual_check_fallback() 45 | .build() 46 | ) 47 | 48 | assert result.config.manual_check == "FALLBACK" 49 | 50 | def test_should_build_with_manual_check_never(self): 51 | result = ( 52 | RequestedDocumentAuthenticityCheckBuilder() 53 | .with_manual_check_never() 54 | .build() 55 | ) 56 | 57 | assert result.config.manual_check == "NEVER" 58 | 59 | 60 | if __name__ == "__main__": 61 | unittest.main() 62 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/check/test_requested_document_comparison_check.py: -------------------------------------------------------------------------------- 1 | import json 2 | import unittest 3 | 4 | from yoti_python_sdk.doc_scan.session.create.check import ( 5 | RequestedIDDocumentComparisonCheckBuilder, 6 | ) 7 | from yoti_python_sdk.doc_scan.session.create.check.document_comparison import ( 8 | RequestedIDDocumentComparisonCheck, 9 | ) 10 | from yoti_python_sdk.doc_scan.session.create.check.document_comparison import ( 11 | RequestedIDDocumentComparisonCheckConfig, 12 | ) 13 | from yoti_python_sdk.doc_scan.session.create.check.document_comparison import ( 14 | RequestedIDDocumentComparisonCheckBuilder, 15 | ) 16 | from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck 17 | from yoti_python_sdk.utils import YotiEncoder 18 | 19 | 20 | def test_should_build_correctly(): 21 | result = RequestedIDDocumentComparisonCheckBuilder().build() 22 | 23 | assert isinstance(result, RequestedCheck) 24 | assert isinstance(result, RequestedIDDocumentComparisonCheck) 25 | assert isinstance(result.config, RequestedIDDocumentComparisonCheckConfig) 26 | assert result.type == "ID_DOCUMENT_COMPARISON" 27 | 28 | 29 | def test_should_serialize_to_json_without_error(): 30 | result = RequestedIDDocumentComparisonCheckBuilder().build() 31 | 32 | s = json.dumps(result, cls=YotiEncoder) 33 | assert s is not None and s != "" 34 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/filter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/session/create/filter/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/filter/test_document_restriction_builder.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from yoti_python_sdk.doc_scan.session.create.filter import DocumentRestrictionBuilder 4 | from yoti_python_sdk.utils import YotiEncoder 5 | 6 | 7 | def test_should_build_without_setting_properties(): 8 | result = DocumentRestrictionBuilder().build() 9 | 10 | assert result.document_types is None 11 | assert result.country_codes is None 12 | 13 | 14 | def test_with_country_codes(): 15 | result = ( 16 | DocumentRestrictionBuilder().with_country_codes(["GBR", "USA", "UKR"]).build() 17 | ) 18 | 19 | assert len(result.country_codes) == 3 20 | assert result.country_codes == ["GBR", "USA", "UKR"] 21 | 22 | 23 | def test_with_document_types(): 24 | result = ( 25 | DocumentRestrictionBuilder() 26 | .with_document_types(["PASSPORT", "DRIVING_LICENCE"]) 27 | .build() 28 | ) 29 | 30 | assert len(result.document_types) == 2 31 | assert result.document_types == ["PASSPORT", "DRIVING_LICENCE"] 32 | 33 | 34 | def test_to_json_should_not_throw_exception(): 35 | result = ( 36 | DocumentRestrictionBuilder() 37 | .with_country_codes(["GBR", "USA", "UKR"]) 38 | .with_document_types(["PASSPORT", "DRIVING_LICENCE"]) 39 | .build() 40 | ) 41 | 42 | s = json.dumps(result, cls=YotiEncoder) 43 | assert s is not None and s != "" 44 | 45 | 46 | def test_to_json_should_not_include_null_values(): 47 | result = ( 48 | DocumentRestrictionBuilder() 49 | .with_document_types(["PASSPORT", "DRIVING_LICENCE"]) 50 | .build() 51 | ) 52 | 53 | s = json.dumps(result, cls=YotiEncoder) 54 | assert s is not None 55 | assert "null" not in s 56 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/filter/test_document_restrictions_filter.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from mock import Mock 4 | 5 | from yoti_python_sdk.doc_scan.session.create.filter import ( 6 | DocumentRestrictionsFilterBuilder, 7 | ) 8 | from yoti_python_sdk.doc_scan.session.create.filter.document_restrictions_filter import ( 9 | DocumentRestriction, 10 | ) 11 | from yoti_python_sdk.utils import YotiEncoder 12 | 13 | 14 | def test_should_set_inclusion_to_whitelist(): 15 | result = DocumentRestrictionsFilterBuilder().for_whitelist().build() 16 | 17 | assert result.inclusion == "WHITELIST" 18 | 19 | 20 | def test_should_set_inclusion_to_blacklist(): 21 | result = DocumentRestrictionsFilterBuilder().for_blacklist().build() 22 | 23 | assert result.inclusion == "BLACKLIST" 24 | 25 | 26 | def test_should_accept_document_restriction(): 27 | document_restriction_mock = Mock(spec=DocumentRestriction) 28 | 29 | result = ( 30 | DocumentRestrictionsFilterBuilder() 31 | .for_whitelist() 32 | .with_document_restriction(document_restriction_mock) 33 | .build() 34 | ) 35 | 36 | assert len(result.documents) == 1 37 | assert result.documents[0] == document_restriction_mock 38 | 39 | 40 | def test_should_accept_multiple_document_restrictions(): 41 | document_restriction_mock = Mock(spec=DocumentRestriction) 42 | other_document_restriction_mock = Mock(spec=DocumentRestriction) 43 | 44 | result = ( 45 | DocumentRestrictionsFilterBuilder() 46 | .for_whitelist() 47 | .with_document_restriction(document_restriction_mock) 48 | .with_document_restriction(other_document_restriction_mock) 49 | .build() 50 | ) 51 | 52 | assert len(result.documents) == 2 53 | 54 | 55 | def test_to_json_should_not_throw_exception(): 56 | document_restriction_mock = Mock(spec=DocumentRestriction) 57 | document_restriction_mock.to_json.return_value = {} 58 | 59 | result = ( 60 | DocumentRestrictionsFilterBuilder() 61 | .for_whitelist() 62 | .with_document_restriction(document_restriction_mock) 63 | .build() 64 | ) 65 | 66 | s = json.dumps(result, cls=YotiEncoder) 67 | assert s is not None and s != "" 68 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/filter/test_orthogonal_restrictions_filter.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from yoti_python_sdk.doc_scan.session.create.filter import ( 4 | OrthogonalRestrictionsFilterBuilder, 5 | ) 6 | from yoti_python_sdk.doc_scan.session.create.filter.orthogonal_restrictions_filter import ( 7 | CountryRestriction, 8 | TypeRestriction, 9 | ) 10 | from yoti_python_sdk.utils import YotiEncoder 11 | 12 | 13 | def test_should_create_correct_country_restriction(): 14 | result = ( 15 | OrthogonalRestrictionsFilterBuilder() 16 | .with_whitelisted_country_codes(["GBR", "USA"]) 17 | .build() 18 | ) 19 | 20 | assert isinstance(result.country_restriction, CountryRestriction) 21 | assert result.country_restriction.inclusion == "WHITELIST" 22 | assert result.country_restriction.country_codes == ["GBR", "USA"] 23 | 24 | 25 | def test_should_create_correct_type_restriction(): 26 | result = ( 27 | OrthogonalRestrictionsFilterBuilder() 28 | .with_whitelisted_document_types(["PASSPORT", "DRIVING_LICENCE"]) 29 | .build() 30 | ) 31 | 32 | assert isinstance(result.type_restriction, TypeRestriction) 33 | assert result.type_restriction.inclusion == "WHITELIST" 34 | assert result.type_restriction.document_types == ["PASSPORT", "DRIVING_LICENCE"] 35 | 36 | 37 | def test_should_set_inclusion_to_whitelist(): 38 | result = ( 39 | OrthogonalRestrictionsFilterBuilder() 40 | .with_whitelisted_document_types([]) 41 | .with_whitelisted_country_codes([]) 42 | .build() 43 | ) 44 | 45 | assert result.country_restriction.inclusion == "WHITELIST" 46 | assert result.type_restriction.inclusion == "WHITELIST" 47 | 48 | 49 | def test_should_set_inclusion_to_blacklist(): 50 | result = ( 51 | OrthogonalRestrictionsFilterBuilder() 52 | .with_blacklisted_country_codes([]) 53 | .with_blacklisted_document_types([]) 54 | .build() 55 | ) 56 | 57 | assert result.country_restriction.inclusion == "BLACKLIST" 58 | assert result.type_restriction.inclusion == "BLACKLIST" 59 | 60 | 61 | def test_to_json_should_not_throw_exception(): 62 | result = ( 63 | OrthogonalRestrictionsFilterBuilder() 64 | .with_whitelisted_document_types(["PASSPORT", "DRIVING_LICENCE"]) 65 | .with_whitelisted_country_codes(["GBR", "USA", "UKR"]) 66 | .build() 67 | ) 68 | 69 | s = json.dumps(result, cls=YotiEncoder) 70 | assert s is not None and s != "" 71 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/filter/test_required_document.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from yoti_python_sdk.doc_scan.session.create.filter.required_document import ( 4 | RequiredDocument, 5 | ) 6 | 7 | 8 | def test_should_not_allow_direct_instantiation(): 9 | with pytest.raises(TypeError) as e: 10 | RequiredDocument() 11 | 12 | assert str(e.value) == "RequiredDocument may not be instantiated" 13 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/filter/test_required_id_document.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from mock import Mock 4 | 5 | from yoti_python_sdk.doc_scan.session.create.filter.document_filter import ( 6 | DocumentFilter, 7 | ) 8 | from yoti_python_sdk.doc_scan.session.create.filter.orthogonal_restrictions_filter import ( 9 | OrthogonalRestrictionsFilter, 10 | ) 11 | from yoti_python_sdk.doc_scan.session.create.filter.required_document import ( 12 | RequiredDocument, 13 | ) 14 | from yoti_python_sdk.doc_scan.session.create.filter.required_id_document import ( 15 | RequiredIdDocument, 16 | RequiredIdDocumentBuilder, 17 | ) 18 | from yoti_python_sdk.utils import YotiEncoder 19 | 20 | 21 | def test_should_allow_direct_instantiation(): 22 | doc_filter_mock = Mock(spec=OrthogonalRestrictionsFilter) 23 | 24 | result = RequiredIdDocument(doc_filter_mock) 25 | assert result.type == "ID_DOCUMENT" 26 | assert result.filter == doc_filter_mock 27 | 28 | 29 | def test_builder_should_accept_any_document_filter(): 30 | doc_filter = DocumentFilter("SOME_FILTER") 31 | 32 | result = RequiredIdDocumentBuilder().with_filter(doc_filter).build() 33 | 34 | assert isinstance(result, RequiredDocument) 35 | assert result.filter == doc_filter 36 | 37 | 38 | def test_to_json_should_not_raise_exception(): 39 | doc_filter = DocumentFilter("SOME_FILTER") 40 | 41 | result = RequiredIdDocumentBuilder().with_filter(doc_filter).build() 42 | 43 | s = json.dumps(result, cls=YotiEncoder) 44 | assert s is not None and s != "" 45 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/objective/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/session/create/objective/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/objective/test_proof_of_address_objective.py: -------------------------------------------------------------------------------- 1 | import json 2 | import unittest 3 | 4 | from yoti_python_sdk.doc_scan.session.create.objective.objective import Objective 5 | from yoti_python_sdk.doc_scan.session.create.objective.proof_of_address_objective import ( 6 | ProofOfAddressObjective, 7 | ) 8 | from yoti_python_sdk.doc_scan.session.create.objective import ( 9 | ProofOfAddressObjectiveBuilder, 10 | ) 11 | from yoti_python_sdk.utils import YotiEncoder 12 | 13 | 14 | class ProofOfAddressObjectiveTest(unittest.TestCase): 15 | def test_builder_builds_proof_of_address_objective(self): 16 | result = ProofOfAddressObjectiveBuilder().build() 17 | 18 | assert isinstance(result, Objective) 19 | assert isinstance(result, ProofOfAddressObjective) 20 | assert result.type == "PROOF_OF_ADDRESS" 21 | 22 | def test_to_json_contains_type(self): 23 | result = ProofOfAddressObjectiveBuilder().build() 24 | 25 | assert result.to_json().get("type") == "PROOF_OF_ADDRESS" 26 | 27 | def test_should_serialize_to_json_without_error(self): 28 | result = ProofOfAddressObjectiveBuilder().build() 29 | 30 | s = json.dumps(result, cls=YotiEncoder) 31 | assert s is not None and s != "" 32 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/create/task/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/session/create/task/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/session/retrieve/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_breakdown_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.breakdown_response import ( 4 | BreakdownResponse, 5 | ) 6 | 7 | 8 | class BreakdownResponseTest(unittest.TestCase): 9 | SOME_SUB_CHECK = "someSubCheck" 10 | SOME_RESULT = "someResult" 11 | SOME_DETAILS = [ 12 | {"name": "firstDetailName", "value": "firstDetailValue"}, 13 | {"name": "secondDetailName", "value": "secondDetailValue"}, 14 | ] 15 | 16 | def test_should_build_correctly(self): 17 | data = { 18 | "sub_check": self.SOME_SUB_CHECK, 19 | "result": self.SOME_RESULT, 20 | "details": self.SOME_DETAILS, 21 | } 22 | 23 | result = BreakdownResponse(data) 24 | 25 | assert result.sub_check is self.SOME_SUB_CHECK 26 | assert result.result is self.SOME_RESULT 27 | assert len(result.details) == 2 28 | assert result.details[0].name == "firstDetailName" 29 | assert result.details[0].value == "firstDetailValue" 30 | 31 | def test_should_default_details_to_empty_list(self): 32 | result = BreakdownResponse({}) 33 | assert len(result.details) == 0 34 | 35 | 36 | if __name__ == "__main__": 37 | unittest.main() 38 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_create_session_result.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.create_session_result import ( 4 | CreateSessionResult, 5 | ) 6 | 7 | 8 | class CreateSessionResultTest(unittest.TestCase): 9 | SOME_CLIENT_SESSION_TOKEN_TTL = 300 10 | SOME_CLIENT_SESSION_TOKEN = "someClientSessionToken" 11 | SOME_SESSION_ID = "someSessionId" 12 | 13 | def test_should_build_correctly(self): 14 | data = { 15 | "client_session_token_ttl": self.SOME_CLIENT_SESSION_TOKEN_TTL, 16 | "client_session_token": self.SOME_CLIENT_SESSION_TOKEN, 17 | "session_id": self.SOME_SESSION_ID, 18 | } 19 | 20 | result = CreateSessionResult(data) 21 | 22 | assert result.client_session_token_ttl is self.SOME_CLIENT_SESSION_TOKEN_TTL 23 | assert result.client_session_token is self.SOME_CLIENT_SESSION_TOKEN 24 | assert result.session_id is self.SOME_SESSION_ID 25 | 26 | def test_should_parse_when_given_none(self): 27 | result = CreateSessionResult(None) 28 | 29 | assert result.client_session_token_ttl is None 30 | assert result.client_session_token is None 31 | assert result.session_id is None 32 | 33 | 34 | if __name__ == "__main__": 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_document_fields_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.document_fields_response import ( 4 | DocumentFieldsResponse, 5 | ) 6 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 7 | 8 | 9 | class DocumentFieldsResponseTest(unittest.TestCase): 10 | def test_should_parse_correctly(self): 11 | data = {"media": {}} 12 | 13 | result = DocumentFieldsResponse(data) 14 | assert isinstance(result.media, MediaResponse) 15 | 16 | def test_should_not_throw_exception_for_none(self): 17 | result = DocumentFieldsResponse(None) 18 | assert result.media is None 19 | 20 | 21 | if __name__ == "__main__": 22 | unittest.main() 23 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_document_id_photo_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.document_id_photo_response import ( 4 | DocumentIdPhotoResponse, 5 | ) 6 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 7 | 8 | 9 | class DocumentIdPhotoResponseTest(unittest.TestCase): 10 | def test_should_parse_correctly(self): 11 | data = {"media": {}} 12 | 13 | result = DocumentIdPhotoResponse(data) 14 | assert isinstance(result.media, MediaResponse) 15 | 16 | def test_should_not_throw_exception_for_none(self): 17 | result = DocumentIdPhotoResponse(None) 18 | assert result.media is None 19 | 20 | 21 | if __name__ == "__main__": 22 | unittest.main() 23 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_face_map_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.face_map_response import FaceMapResponse 4 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 5 | 6 | 7 | class FaceMapResponseTest(unittest.TestCase): 8 | def test_should_build_correctly(self): 9 | data = {"media": {}} 10 | 11 | result = FaceMapResponse(data) 12 | assert isinstance(result.media, MediaResponse) 13 | 14 | def test_should_parse_with_none(self): 15 | result = FaceMapResponse(None) 16 | assert result.media is None 17 | 18 | 19 | if __name__ == "__main__": 20 | unittest.main() 21 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_file_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.file_response import FileResponse 4 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 5 | 6 | 7 | class FileResponseTest(unittest.TestCase): 8 | def test_should_parse_correctly(self): 9 | data = {"media": {}} 10 | 11 | result = FileResponse(data) 12 | assert isinstance(result.media, MediaResponse) 13 | 14 | def test_should_parse_when_none(self): 15 | result = FileResponse(None) 16 | assert isinstance(result, FileResponse) 17 | assert result.media is None 18 | 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_frame_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.frame_response import FrameResponse 4 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 5 | 6 | 7 | class FrameResponseTest(unittest.TestCase): 8 | def test_should_parse_correctly(self): 9 | data = {"media": {}} 10 | 11 | result = FrameResponse(data) 12 | assert isinstance(result.media, MediaResponse) 13 | 14 | def test_should_parse_when_none(self): 15 | result = FrameResponse(None) 16 | assert isinstance(result, FrameResponse) 17 | assert result.media is None 18 | 19 | 20 | if __name__ == "__main__": 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_generated_check_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.generated_check_response import ( 4 | GeneratedCheckResponse, 5 | ) 6 | 7 | 8 | class GeneratedCheckResponseTest(unittest.TestCase): 9 | SOME_ID = "someId" 10 | SOME_TYPE = "someType" 11 | 12 | def test_should_parse_correctly(self): 13 | data = {"id": self.SOME_ID, "type": self.SOME_TYPE} 14 | 15 | result = GeneratedCheckResponse(data) 16 | 17 | assert result.id is self.SOME_ID 18 | assert result.type is self.SOME_TYPE 19 | 20 | def test_should_parse_when_none(self): 21 | result = GeneratedCheckResponse(None) 22 | 23 | assert isinstance(result, GeneratedCheckResponse) 24 | assert result.id is None 25 | assert result.type is None 26 | 27 | 28 | if __name__ == "__main__": 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_generated_media.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.generated_media import GeneratedMedia 4 | 5 | 6 | class GeneratedMediaTest(unittest.TestCase): 7 | SOME_ID = "someId" 8 | SOME_TYPE = "someType" 9 | 10 | def test_should_parse_correctly(self): 11 | data = {"id": self.SOME_ID, "type": self.SOME_TYPE} 12 | 13 | result = GeneratedMedia(data) 14 | 15 | assert result.id is self.SOME_ID 16 | assert result.type is self.SOME_TYPE 17 | 18 | def test_should_parse_with_none(self): 19 | result = GeneratedMedia(None) 20 | 21 | assert isinstance(result, GeneratedMedia) 22 | assert result.id is None 23 | assert result.type is None 24 | 25 | 26 | if __name__ == "__main__": 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_liveness_resource_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.face_map_response import FaceMapResponse 4 | from yoti_python_sdk.doc_scan.session.retrieve.liveness_resource_response import ( 5 | ZoomLivenessResourceResponse, 6 | LivenessResourceResponse, 7 | ) 8 | 9 | 10 | class LivenessResourceResponseTest(unittest.TestCase): 11 | def test_should_not_throw_exception_if_data_is_none(self): 12 | result = LivenessResourceResponse(None) 13 | 14 | assert result.liveness_type is None 15 | assert result.id is None 16 | assert len(result.tasks) == 0 17 | 18 | 19 | class ZoomLivenessResourceResponseTest(unittest.TestCase): 20 | SOME_ID = "someId" 21 | SOME_FRAMES = [{"first": "frame"}, {"second": "frame"}] 22 | 23 | def test_should_retain_liveness_type(self): 24 | data = { 25 | "id": self.SOME_ID, 26 | "facemap": {}, 27 | "frames": self.SOME_FRAMES, 28 | "liveness_type": "ZOOM", 29 | } 30 | 31 | result = ZoomLivenessResourceResponse(data) 32 | 33 | assert result.liveness_type == "ZOOM" 34 | 35 | def test_zoom_liveness_should_parse_correctly(self): 36 | data = {"id": self.SOME_ID, "facemap": {}, "frames": self.SOME_FRAMES} 37 | 38 | result = ZoomLivenessResourceResponse(data) 39 | 40 | assert result.id is self.SOME_ID 41 | assert isinstance(result.facemap, FaceMapResponse) 42 | assert len(result.frames) == 2 43 | 44 | def test_should_parse_with_none(self): 45 | result = ZoomLivenessResourceResponse(None) 46 | 47 | assert result.id is None 48 | assert len(result.tasks) == 0 49 | assert result.facemap is None 50 | assert len(result.frames) == 0 51 | 52 | 53 | if __name__ == "__main__": 54 | unittest.main() 55 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_media_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from datetime import datetime 3 | 4 | import pytz 5 | 6 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 7 | 8 | 9 | class MediaResponseTest(unittest.TestCase): 10 | SOME_ID = "someId" 11 | SOME_TYPE = "someType" 12 | SOME_CREATED = "2019-05-01T05:01:48.000Z" 13 | SOME_LAST_UPDATED = "2019-05-01T05:01:48.000Z" 14 | 15 | EXPECTED_DATETIME = datetime( 16 | year=2019, 17 | month=5, 18 | day=1, 19 | hour=5, 20 | minute=1, 21 | second=48, 22 | microsecond=0, 23 | tzinfo=pytz.utc, 24 | ) 25 | 26 | def test_should_parse_correctly(self): 27 | data = { 28 | "id": self.SOME_ID, 29 | "type": self.SOME_TYPE, 30 | "created": self.SOME_CREATED, 31 | "last_updated": self.SOME_LAST_UPDATED, 32 | } 33 | 34 | result = MediaResponse(data) 35 | 36 | assert result.id is self.SOME_ID 37 | assert result.type is self.SOME_TYPE 38 | assert result.created == self.EXPECTED_DATETIME 39 | assert result.last_updated == self.EXPECTED_DATETIME 40 | 41 | def test_should_parse_with_none(self): 42 | result = MediaResponse(None) 43 | 44 | assert result.id is None 45 | assert result.type is None 46 | assert result.created is None 47 | assert result.last_updated is None 48 | 49 | def test_should_set_dates_as_none_for_invalid_format(self): 50 | data = {"created": "someInvalidFormat", "last_updated": "someInvalidFormat"} 51 | 52 | result = MediaResponse(data) 53 | 54 | assert result.created is None 55 | assert result.last_updated is None 56 | 57 | 58 | if __name__ == "__main__": 59 | unittest.main() 60 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_media_value.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import unittest 3 | 4 | from yoti_python_sdk.doc_scan.session.retrieve.media_value import MediaValue 5 | 6 | 7 | class MediaValueTest(unittest.TestCase): 8 | SOME_MIME_TYPE = "someMimeType" 9 | SOME_CONTENT = b"someByteArray" 10 | 11 | def test_should_parse_correctly(self): 12 | result = MediaValue(self.SOME_MIME_TYPE, self.SOME_CONTENT) 13 | 14 | assert result.mime_type is self.SOME_MIME_TYPE 15 | assert result.content is self.SOME_CONTENT 16 | 17 | expected = ( 18 | "data:" 19 | + self.SOME_MIME_TYPE 20 | + ";base64," 21 | + base64.b64encode(self.SOME_CONTENT).decode("utf-8") 22 | ) 23 | 24 | assert result.base64_content == expected 25 | 26 | 27 | if __name__ == "__main__": 28 | unittest.main() 29 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_page_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.media_response import MediaResponse 4 | from yoti_python_sdk.doc_scan.session.retrieve.page_response import PageResponse 5 | from yoti_python_sdk.doc_scan.session.retrieve.frame_response import FrameResponse 6 | 7 | 8 | class PageResponseTest(unittest.TestCase): 9 | SOME_CAPTURE_METHOD = "someCaptureMethod" 10 | SOME_FRAMES = [{"first": "frame"}, {"second": "frame"}] 11 | 12 | def test_should_parse_correctly(self): 13 | data = { 14 | "capture_method": self.SOME_CAPTURE_METHOD, 15 | "media": {}, 16 | "frames": self.SOME_FRAMES, 17 | } 18 | 19 | result = PageResponse(data) 20 | 21 | assert result.capture_method is self.SOME_CAPTURE_METHOD 22 | assert isinstance(result.media, MediaResponse) 23 | assert len(result.frames) == 2 24 | assert isinstance(result.frames[0], FrameResponse) 25 | assert isinstance(result.frames[1], FrameResponse) 26 | 27 | def test_should_parse_with_none(self): 28 | result = PageResponse(None) 29 | 30 | assert result.capture_method is None 31 | assert result.media is None 32 | assert len(result.frames) == 0 33 | 34 | 35 | if __name__ == "__main__": 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_recommendation_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.recommendation_response import ( 4 | RecommendationResponse, 5 | ) 6 | 7 | 8 | class RecommendationResponseTest(unittest.TestCase): 9 | SOME_VALUE = "someValue" 10 | SOME_REASON = "someReason" 11 | SOME_RECOVERY_SUGGESTION = "someRecoverySuggestion" 12 | 13 | def test_should_parse_correctly(self): 14 | data = { 15 | "value": self.SOME_VALUE, 16 | "reason": self.SOME_REASON, 17 | "recovery_suggestion": self.SOME_RECOVERY_SUGGESTION, 18 | } 19 | 20 | result = RecommendationResponse(data) 21 | 22 | assert result.value is self.SOME_VALUE 23 | assert result.reason is self.SOME_REASON 24 | assert result.recovery_suggestion is self.SOME_RECOVERY_SUGGESTION 25 | 26 | def test_should_parse_with_none(self): 27 | result = RecommendationResponse(None) 28 | 29 | assert result.value is None 30 | assert result.reason is None 31 | assert result.recovery_suggestion is None 32 | 33 | 34 | if __name__ == "__main__": 35 | unittest.main() 36 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_report_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.recommendation_response import ( 4 | RecommendationResponse, 5 | ) 6 | from yoti_python_sdk.doc_scan.session.retrieve.report_response import ReportResponse 7 | 8 | 9 | class ReportResponseTest(unittest.TestCase): 10 | def test_should_parse_correctly(self): 11 | data = { 12 | "recommendation": {"some": "recommendation"}, 13 | "breakdown": [{"first": "breakdown"}, {"second": "breakdown"}], 14 | } 15 | 16 | result = ReportResponse(data) 17 | 18 | assert isinstance(result.recommendation, RecommendationResponse) 19 | assert len(result.breakdown) == 2 20 | 21 | def test_should_parse_with_none(self): 22 | result = ReportResponse(None) 23 | 24 | assert result.recommendation is None 25 | assert len(result.breakdown) == 0 26 | 27 | 28 | if __name__ == "__main__": 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_container.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.liveness_resource_response import ( 4 | LivenessResourceResponse, 5 | ) 6 | from yoti_python_sdk.doc_scan.session.retrieve.liveness_resource_response import ( 7 | ZoomLivenessResourceResponse, 8 | ) 9 | from yoti_python_sdk.doc_scan.session.retrieve.resource_container import ( 10 | ResourceContainer, 11 | ) 12 | 13 | 14 | class ResourceContainerTest(unittest.TestCase): 15 | def test_should_parse_correctly(self): 16 | data = { 17 | "id_documents": [{"first": "id_document"}, {"second": "id_document"}], 18 | "supplementary_documents": [{"first": "document"}, {"second": "document"}], 19 | "liveness_capture": [ 20 | {"liveness_type": "ZOOM"}, 21 | {"liveness_type": "someUnknown"}, 22 | ], 23 | } 24 | 25 | result = ResourceContainer(data) 26 | 27 | assert len(result.id_documents) == 2 28 | assert len(result.supplementary_documents) == 2 29 | assert len(result.liveness_capture) == 2 30 | assert isinstance(result.liveness_capture[0], ZoomLivenessResourceResponse) 31 | assert isinstance(result.liveness_capture[1], LivenessResourceResponse) 32 | 33 | def test_should_parse_with_none(self): 34 | result = ResourceContainer(None) 35 | 36 | assert len(result.id_documents) == 0 37 | assert len(result.liveness_capture) == 0 38 | 39 | def test_should_filter_zoom_liveness_resources(self): 40 | data = { 41 | "liveness_capture": [ 42 | {"liveness_type": "ZOOM"}, 43 | {"liveness_type": "someUnknown"}, 44 | ] 45 | } 46 | 47 | result = ResourceContainer(data) 48 | 49 | assert len(result.liveness_capture) == 2 50 | assert len(result.zoom_liveness_resources) == 1 51 | 52 | 53 | if __name__ == "__main__": 54 | unittest.main() 55 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/session/retrieve/test_resource_response.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from yoti_python_sdk.doc_scan.session.retrieve.resource_response import ResourceResponse 4 | from yoti_python_sdk.doc_scan.session.retrieve.task_response import TaskResponse 5 | from yoti_python_sdk.doc_scan.session.retrieve.task_response import ( 6 | TextExtractionTaskResponse, 7 | ) 8 | 9 | 10 | class ResourceResponseTest(unittest.TestCase): 11 | SOME_ID = "someId" 12 | 13 | def test_should_parse_correctly(self): 14 | data = { 15 | "id": self.SOME_ID, 16 | "tasks": [ 17 | {"type": "ID_DOCUMENT_TEXT_DATA_EXTRACTION"}, 18 | {"type": "someUnknownType"}, 19 | ], 20 | } 21 | 22 | result = ResourceResponse(data) 23 | 24 | assert result.id is self.SOME_ID 25 | assert len(result.tasks) == 2 26 | assert isinstance(result.tasks[0], TextExtractionTaskResponse) 27 | assert isinstance(result.tasks[1], TaskResponse) 28 | 29 | def test_should_parse_with_none(self): 30 | result = ResourceResponse(None) 31 | 32 | assert result.id is None 33 | assert len(result.tasks) == 0 34 | 35 | 36 | if __name__ == "__main__": 37 | unittest.main() 38 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/support/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/doc_scan/support/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/doc_scan/support/test_supported_documents.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk.doc_scan.support.supported_documents import ( 2 | SupportedCountry, 3 | SupportedDocument, 4 | SupportedDocumentsResponse, 5 | ) 6 | 7 | 8 | def test_supported_document_should_parse_data(): 9 | data = {"type": "someSupportedDocument"} 10 | 11 | result = SupportedDocument(data) 12 | 13 | assert result.type == "someSupportedDocument" 14 | 15 | 16 | def test_supported_document_should_not_throw_exception_on_missing_data(): 17 | result = SupportedDocument(None) 18 | assert result.type is None 19 | 20 | 21 | def test_supported_country_should_parse_data(): 22 | data = { 23 | "code": "someCode", 24 | "supported_documents": [{"type": "firstType"}, {"type": "secondType"}], 25 | } 26 | 27 | result = SupportedCountry(data) 28 | 29 | assert result.code == "someCode" 30 | assert len(result.supported_documents) == 2 31 | 32 | 33 | def test_supported_country_should_not_throw_exception_on_missing_data(): 34 | result = SupportedCountry(None) 35 | 36 | assert result.code is None 37 | assert len(result.supported_documents) == 0 38 | 39 | 40 | def test_supported_document_response_should_parse_data(): 41 | data = {"supported_countries": [{"code": "GBR"}, {"code": "USA"}]} 42 | 43 | result = SupportedDocumentsResponse(data) 44 | 45 | assert len(result.supported_countries) == 2 46 | 47 | 48 | def test_supported_document_response_should_not_throw_exception_on_missing_data(): 49 | result = SupportedDocumentsResponse(None) 50 | 51 | assert len(result.supported_countries) == 0 52 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/dynamic_sharing_service/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/dynamic_sharing_service/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/dynamic_sharing_service/extension/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/dynamic_sharing_service/extension/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/dynamic_sharing_service/extension/test_extension_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.dynamic_sharing_service.extension.extension_builder import ( 5 | ExtensionBuilder, 6 | ) 7 | 8 | 9 | def test_build_simple_extension(): 10 | EXTENSION_TYPE = "TEST" 11 | EXTENSION_CONTENT = 99 12 | 13 | extension = ( 14 | ExtensionBuilder() 15 | .with_extension_type(EXTENSION_TYPE) 16 | .with_content(EXTENSION_CONTENT) 17 | .build() 18 | ) 19 | 20 | assert extension["type"] == EXTENSION_TYPE 21 | assert extension["content"] == EXTENSION_CONTENT 22 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/dynamic_sharing_service/extension/test_transactional_flow_extension_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.dynamic_sharing_service.extension.transactional_flow_extension_builder import ( 5 | TransactionalFlowExtensionBuilder, 6 | ) 7 | 8 | 9 | def test_should_build(): 10 | TRANSACTIONAL_FLOW = "TRANSACTIONAL_FLOW" 11 | EXTENSION_CONTENT = 99 12 | 13 | extension = ( 14 | TransactionalFlowExtensionBuilder().with_content(EXTENSION_CONTENT).build() 15 | ) 16 | 17 | assert extension["type"] == TRANSACTIONAL_FLOW 18 | assert extension["content"] == EXTENSION_CONTENT 19 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/dynamic_sharing_service/policy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/dynamic_sharing_service/policy/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/dynamic_sharing_service/policy/test_source_constraint_builder.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk.dynamic_sharing_service.policy.source_constraint_builder import ( 2 | SourceConstraintBuilder, 3 | ) 4 | from yoti_python_sdk.config import ANCHOR_VALUE_DRIVING_LICENCE, ANCHOR_VALUE_PASSPORT 5 | 6 | 7 | def test_build(): 8 | constraint = SourceConstraintBuilder().build() 9 | 10 | assert constraint["type"] == "SOURCE" 11 | assert not constraint["preferred_sources"]["soft_preference"] 12 | assert constraint["preferred_sources"]["anchors"] == [] 13 | 14 | 15 | def test_with_driving_licence(): 16 | constraint = SourceConstraintBuilder().with_driving_licence().build() 17 | 18 | anchors = constraint["preferred_sources"]["anchors"] 19 | assert len(anchors) == 1 20 | assert ANCHOR_VALUE_DRIVING_LICENCE in [a["name"] for a in anchors] 21 | 22 | 23 | def test_with_soft_preference(): 24 | constraint = ( 25 | SourceConstraintBuilder() 26 | .with_passport() 27 | .with_driving_licence() 28 | .with_soft_preference() 29 | .build() 30 | ) 31 | anchors = constraint["preferred_sources"]["anchors"] 32 | assert len(anchors) == 2 33 | assert ANCHOR_VALUE_DRIVING_LICENCE in [a["name"] for a in anchors] 34 | assert ANCHOR_VALUE_PASSPORT in [a["name"] for a in anchors] 35 | assert constraint["preferred_sources"]["soft_preference"] 36 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/dynamic_sharing_service/policy/test_wanted_anchor_builder.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk.dynamic_sharing_service.policy.wanted_anchor_builder import ( 2 | WantedAnchorBuilder, 3 | ) 4 | 5 | 6 | def test_build(): 7 | TEST_VALUE = "TEST VALUE" 8 | TEST_SUB_TYPE = "TEST SUB TYPE" 9 | 10 | builder = WantedAnchorBuilder() 11 | builder.with_value(TEST_VALUE) 12 | builder.with_subtype(TEST_SUB_TYPE) 13 | 14 | anchor = builder.build() 15 | 16 | assert anchor["name"] == TEST_VALUE 17 | assert anchor["sub_type"] == TEST_SUB_TYPE 18 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/dynamic_sharing_service/policy/test_wanted_attribute_builder.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk.dynamic_sharing_service.policy.wanted_attribute_builder import ( 2 | WantedAttributeBuilder, 3 | ) 4 | from yoti_python_sdk.dynamic_sharing_service.policy.source_constraint_builder import ( 5 | SourceConstraintBuilder, 6 | ) 7 | 8 | import pytest 9 | 10 | 11 | def test_build(): 12 | NAME = "Test name" 13 | DERIVATION = "Test derivation" 14 | 15 | builder = WantedAttributeBuilder() 16 | 17 | attribute = builder.with_name(NAME).with_derivation(DERIVATION).build() 18 | 19 | assert attribute["name"] == NAME 20 | assert attribute["derivation"] == DERIVATION 21 | 22 | 23 | def test_with_constraint(): 24 | constraint = SourceConstraintBuilder().with_driving_licence().build() 25 | attribute = ( 26 | WantedAttributeBuilder() 27 | .with_name("test name") 28 | .with_constraint(constraint) 29 | .build() 30 | ) 31 | 32 | constraints = attribute["constraints"] 33 | assert len(constraints) == 1 34 | assert len(constraints[0]["preferred_sources"]["anchors"]) == 1 35 | 36 | 37 | def test_with_multiple_constraints(): 38 | constraintA = SourceConstraintBuilder().with_driving_licence().build() 39 | constraintB = SourceConstraintBuilder().with_passport().build() 40 | 41 | attribute = ( 42 | WantedAttributeBuilder() 43 | .with_name("test name") 44 | .with_constraint([constraintA, constraintB]) 45 | .build() 46 | ) 47 | 48 | constraints = attribute["constraints"] 49 | assert len(constraints) == 2 50 | 51 | 52 | def test_missing_attribute_name_raises(): 53 | with pytest.raises(ValueError): 54 | WantedAttributeBuilder().build() 55 | 56 | 57 | def test_acccept_self_assert(): 58 | attribute = ( 59 | WantedAttributeBuilder() 60 | .with_name("test name") 61 | .with_accept_self_asserted() 62 | .build() 63 | ) 64 | assert attribute["accept_self_asserted"] 65 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/dynamic_sharing_service/test_dynamic_scenario_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from yoti_python_sdk.dynamic_sharing_service.dynamic_scenario_builder import ( 5 | DynamicScenarioBuilder, 6 | ) 7 | from yoti_python_sdk.dynamic_sharing_service.policy.dynamic_policy_builder import ( 8 | DynamicPolicyBuilder, 9 | ) 10 | 11 | 12 | def test_build_scenario(): 13 | EXTENSION1 = "Extension 1" 14 | EXTENSION2 = "Extension 2" 15 | CALLBACK_ENDPOINT = "Callback Endpoint" 16 | 17 | scenario = ( 18 | DynamicScenarioBuilder() 19 | .with_policy( 20 | DynamicPolicyBuilder().with_full_name().with_wanted_remember_me().build() 21 | ) 22 | .with_extension(EXTENSION1) 23 | .with_extension(EXTENSION2) 24 | .with_callback_endpoint(CALLBACK_ENDPOINT) 25 | .build() 26 | ) 27 | 28 | assert len(scenario["policy"]["wanted"]) == 1 29 | assert scenario["policy"]["wanted_remember_me"] 30 | assert len(scenario["extensions"]) == 2 31 | assert EXTENSION1 in scenario["extensions"] 32 | assert EXTENSION2 in scenario["extensions"] 33 | assert scenario["callback_endpoint"] == CALLBACK_ENDPOINT 34 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/file_helper.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import io 3 | from os.path import abspath, dirname, join 4 | 5 | FIXTURES_DIR = join(dirname(abspath(__file__)), "fixtures") 6 | 7 | 8 | def get_file_bytes(file_path): 9 | base64_data = read_file(file_path) 10 | file_bytes = binascii.a2b_base64(base64_data) 11 | 12 | return file_bytes 13 | 14 | 15 | def read_file(file_path): 16 | with io.open(file_path, mode="r", encoding="utf-8") as file: 17 | return file.read() 18 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/aml_response.txt: -------------------------------------------------------------------------------- 1 | {"on_fraud_list":false,"on_pep_list":true,"on_watch_list":false} 2 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/anchor_passport.txt: -------------------------------------------------------------------------------- 1 | CjdBTkMtRE9D5oQ/YdIfjbvf1HL/HT7s/Xgse6TlNthXYyfF9knv02vq6Vxd5RafiJbR9xVVl+knEowIMIIECDCCAnCgAwIBAgIRANEL6idR0hcevQr4tmIIcoowDQYJKoZIhvcNAQELBQAwJzElMCMGA1UEAxMccGFzc3BvcnQtcmVnaXN0cmF0aW9uLXNlcnZlcjAeFw0xODA0MDUxNDM1MDFaFw0xODA0MTIxNDM1MDFaMCcxJTAjBgNVBAMTHHBhc3Nwb3J0LXJlZ2lzdHJhdGlvbi1zZXJ2ZXIwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC9q8ZJxaOoeDS5anGhVhQ6Y0Ge47Jv0pmXoaI+rNoO6zkErmJyL2sLNJRRrH2+aqTKXwnjCF10EBld/0ryoOI1Zin6UfuEIi3uCXAVktb8qkpX+JJH+6FRZ0QztNUybfWN2M1BP3P1P3i7jO5Vh7BsQG7WEB8hhn6gAGP/aWaBk79i6Om2/m6qpPCHM9wSDM+L+bpJdrwRgZEdHzyOpMKxUwpIe0D0j6M9e+8gSVnK40aRlIXdjTrmggncDcd9CMRN1oIFJ9YDLFRUYKFp5Hjgfiv2k0uIdyJDOx65VRVROxpfZjh2jgLchr4FBY/WCP8AA8G/usS9EiwRQxZ8+bf/4naJXVFMRWdNLRNX3g7pNZkmLFt6prwOCc9PijLIKlKX3uvjJgAm3/g28VON0g9ys8c4LVLBUg9tYvWtJg2+yNWG7sRr2U0mohTiYWUnf4gnhvsxTNVTWvOY4FltZnJOLlKoaSTyfTIjIGAvFB8P3s3lZDXzRG3QCtInUkASgOUCAwEAAaMvMC0wDgYDVR0PAQH/BAQDAgOYMBsGCysGAQQBgvAXAQEBBAwwCoAIUEFTU1BPUlQwDQYJKoZIhvcNAQELBQADggGBAE/aVEbzKLjDowg6TbRetXoumWbeqhL4y1rkFz6Oig4TutaSLEdIfqzONBa9bfimcJcVyq5PVASflNv770DGMwC5qPj6vFTKWjgMp7e/t7iPnuMic7LlIEVOtZS+eQBCYdBfwC2nY/gTqTaDZdHmK3QPyLyUjcQNplrgdqsk5jekQ3lYnbYUzSm9dLQjxkcAtCq0Ud6fM/GGkDH7wB+WHx6gDAlT3KhPLypkg0tGI8/Ej01FNrfaN7LKWWxfVGXwNjS/HpPJvACjR7wp6asJErO+jUItKvZ772A0AUiOSKjgUJ3NyrYczmxds4IE7bnsedkHsgRc9PDJraGHKrhXyDfZzgPzJ4zQ1iQXx4PicR7Dm7NyeA1zepFW2azRFvht3ge0bKUM+/CuR9GV9HOirXXSEAUTv//S5M3REMJJbstd3tVPR48gpcKWXqUPicg+E8JLCxKvXw+R1OK9yqlW6bnQfUSvI2SafYkixeyHnmk7kP9sAkvSi29oH8n1YH4hPxqFAwgBEoADAdw/1ZI5sbf+2H/tvyEVNmsAjmFHafiKhG2e7c6TmISEXfFTJTi69lT/DBgSHlhxzwpBl3Mc7MEqobd4SX5PBbRzqaGdiWt00C2T359hH0+tHUvxwRq3lTpWoLQ9rsZD0m8fHUYrtv4hrQeipeq7uVoUNmc0vo/Yp6+6lkRECGss3k8/J4rXwrhciBYEuKqhChkXZwbKVU83IbioVRBnbesvNoE0Wwgbcx7+1VAVaDC6zmZ/cmUMdwdsIkT4MXV5FqTlqVc7kRhiLf/iNPEr806mYvR3z26JO8VIjPKKvgoWYucH5g5GFYukpJaG+O3s9wgarmkrhcsx74gitTMgjRYiWSQQ02wpUnj6WWPQ5Zsm6RTcdt9Q3oHxdzWm5DCeMXuS+r0RgGpz4p749uuIGvzs6gJAiR4ye3o22gU/SE6+sGjtc2i0ddjqRjxgmxsSNL9dIy07kDqZ/mK5P4TCxhUPmOYxjhfndl1dBCQleEV0PpMmXXUaKVlCVA+/62PMIgNPQ1IqhQMIARKAA5Q1xoxg3Fq34i3km+zKiU4tpaAcxB//fcRjcXVOvSaJvWvLMMcBkPlny5+lM3fTb8uzs6RMNEWrb+GD3gVbnrzx5Bbc2f/lJlU0EGs0ZsBzSuWsr0qPiYd/oMtXu2Iz3oR8t7C5whUZX9rBlayrm+AceLFJOLdTkVFx8qwJe10brMqoE/1OU4403SILzIkw+nsOKAmjFlymhRZwwDEmBFBf+v8vyDLDeVM8EtmtTLM/FHpgCPsNBL+9UnwHSC+np4kIS3sJMNXHuoS0uxpi/XgFlZSWjPnR8UKzw1iXzA7Dz18Msfv+aHHUF/EtML3SJwDv52ewP6cv6N9pd5XtxJB9D4nB959t7oNTltQKGoIy5wCNOITVo7CzXX7IBwE3Lzp+uvJuetEkEVgjGmUD6PTSK0P4yL56cWwW30jUHXNTkN64ryHhwKvHdvzT+xp/synMnLnPO8X6+BV6sqm7GF+OL4PGE3XO3nZCIPwZ0dgxz6r6BtkfV7pBWIlPPa/2LTJHCAEQ0bvEyui02gIaHFIc8RKJ4U36MiJqXMjQlWXbhVu/URDuYOFXITEiHNs5UaZ0Q8FPlpgca5LurwwVkP/EqVsqzc1tuK06AA== 2 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/auth_digest_get.txt: -------------------------------------------------------------------------------- 1 | AuMNqWmOu8eSmMlI81sKqpAUJhdE7Jkj1GT+G+RSjQE5SftcgNEPduZQihKYoSORtGVpm2dFT2OgndAGWYOdumDPnm2XIpqpG1Ye9X7nVzegxL31l7tFfzkjweHOYkhedTcY9FSfx2j3w+8vc9xRTLHybiJoFGouFPuu5e9y9T1uwYYhjbu/Mj9bgbKBbOo0qeOqJt47Z6YqKaTeYMMqw9KiLCFibNHow7IOzwoxcgegK+P3CcyFCATVA384mr3SlLpCNC67aK0IpXdzdcBK+cegAEu2L0PPDCMe3qmur0aknvPjLPkQwI7SPOWcikmAUDkc01AjWbMOiz8jKKs/yA== -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/auth_digest_post.txt: -------------------------------------------------------------------------------- 1 | GuU/GcEzQGZ2osKvalkP/a+ULJ9sVeu1lAQiaBrZ8iPbf+SGopPzlWVVvzCexVZMSB5VWcvyJm294F9xSax7VrB7nJHWkFV1Gh+6xy3Zjok6Ty3yx52uHczDHPQf8NGWGDlQNoE6ArZDOSmka5/6griD9ipirdeIB44RM1I/TjB1ULLUCBRffMh9ijxKSsR5M9KRu9Z/XV3TEE9NZ5bJX47S2hQQuZXcg2XvUWXU9tVqvMnqIGS3hxVvH3Pk+x0cx45JIxND87IuXxTEnYUZfKhO1uTaEwLNWrtRDSFhOCYuXNprRlNQVqqEUJMT8HHPi2wZJKIl/FEgrSQqegxQGw== -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/auth_key.txt: -------------------------------------------------------------------------------- 1 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs9zAY5K9O92zfmRhxBO0NX8Dg7UyyIaLE5GdbCMimlccew2p8LN6P8EDUoU7hiCbW1EQ/cp4iZVIp7UPA3AO/ecuejs2DjkFQOeMGnSlwD0pk74ZI3ammQtYm2ml47IWGrciMh4dPIPh0SOF+tVD0kHhAB9cMaj96Ij2De60Y7SeqvIXUHCtnoHId7Zk5I71mtewAnb9Gpx+wPnr2gpX/uUqkh+3ZHsF2eNCpw/ICvKj4UkNXopUyBemDp3n/s7u8TFyewp7ipPbFxDmxZKJT9SjZNFFe/jc2V/R2uC9qSFRKpTsxqmXggjiBlH46cpyg2SeYFj1p5bkpKZ10b3iOwIDAQAB -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/encrypted_yoti_token.txt: -------------------------------------------------------------------------------- 1 | c31Db4y6ClxSWy26xDpa9LEX3ZTUuR-rKaAhjQWnmKilR20IshkysR5Y3Hh3R6hanOyxcu7fl5vbjikkGZZb3_iH6NjxmBXuGY_Fr23AhrHvGL9WMg4EtemVvr6VI2f_5H_PDhDpYUvv-YpEM0f_SReoVxGIc8VGfj1gukuhPyNJ9hs55-SDdUjN77JiA6FPcYZxEIaqQE_yT_c3Y4V72Jnq3RHbG0vL6SefSfY_fFsnx_HeddsJc10qJYCwAkdGzVzbJH2DQ2Swp821Gwyj9eNK54S6HvpIg7LclID7BtymG6z7cTNp3fXX7mgKYoQlh_DHmPmaiqyj398w424RBg== -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/response_create_docs_scan_session.txt: -------------------------------------------------------------------------------- 1 | { 2 | "client_session_token_ttl": 599, 3 | "client_session_token": "", 4 | "session_id": "" 5 | } 6 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_image.txt: -------------------------------------------------------------------------------- 1 | QUFBQQ== 2 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/response_get_docs_scan_media_json.txt: -------------------------------------------------------------------------------- 1 | { 2 | "value": "example" 3 | } 4 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/response_share_url.txt: -------------------------------------------------------------------------------- 1 | { 2 | "qrcode": "https://code.yoti.com/forfhq3peurij4ihroiehg4jgiej", 3 | "ref_id" : "01aa2dea-d28b-11e6-bf26-cec0c932ce01" 4 | } 5 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/sdk-test.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAs9zAY5K9O92zfmRhxBO0NX8Dg7UyyIaLE5GdbCMimlccew2p 3 | 8LN6P8EDUoU7hiCbW1EQ/cp4iZVIp7UPA3AO/ecuejs2DjkFQOeMGnSlwD0pk74Z 4 | I3ammQtYm2ml47IWGrciMh4dPIPh0SOF+tVD0kHhAB9cMaj96Ij2De60Y7SeqvIX 5 | UHCtnoHId7Zk5I71mtewAnb9Gpx+wPnr2gpX/uUqkh+3ZHsF2eNCpw/ICvKj4UkN 6 | XopUyBemDp3n/s7u8TFyewp7ipPbFxDmxZKJT9SjZNFFe/jc2V/R2uC9qSFRKpTs 7 | xqmXggjiBlH46cpyg2SeYFj1p5bkpKZ10b3iOwIDAQABAoIBACr0ue4OCavWkxvI 8 | laDio9Ny9j/qcqp5l5Wg3VwKOCVsUJ0C8mdONhAr5MM8lq698tyoS8qRJKCXSrbj 9 | AybrCGmTYQJISeyzqZGKu2dGHKAA+4ERkadqmvdKQms7nCb5TVYsDrqxfoIJbVEp 10 | jsINVRlOKpKA6t/hYGK88yb4r5RwFB7t0qwLQRbI8MajTWmgk9SKFjSiOnFH5+Dl 11 | ZiNDa39mHwY4fu9ZKjenZDq04/eh32mZSUkyQ4V3exqz/Xugl++mcBq9KIv1M489 12 | U4a1hXV+0biAuro6lgvyquHSKpYx9n1verjcBa5J0AWQhnwS2lcsfQIRssV8W0gd 13 | SVdd7QECgYEA4pVr7haKz+2p2GJpzCFGtI5T4FBS+1cCDDX0oPtReDIrPFqCnI6e 14 | nYzF8lbx24B91CoTk4rB4/NZOMI2KjyXo+EAe+yhDhMK2BKU15UVKqpXt2ur+VpY 15 | DBfoQuvZ91PT7f4sX53Y9lrJz6C3+xnI4K97o1GQh+2SAAgo72nZIdsCgYEAyzaH 16 | 5rnVbSFFuYcAxUz3ClZH1shP2vnubnlFKTtV6NjvFGsswTVgeINCJhSxzTHX8VoJ 17 | AauOIxzr87T3RrH1BuN383ZQxlvEbHjbiBcnZrWklLXJEElRhLFcfgvtH01xSxuP 18 | /7WgLMsjnzJ/TE9tm96omf5iBygj5BSIOwclHyECgYEA36fCe6dAqfHcfzzVVatb 19 | EYqT/I0M/A9sdAUmTWkFh/FtgAuPdV3J75YvJgDwh0yT58MIw9BphsqEPWRm9tYM 20 | kLTeN3ThnPTq9VGSHiKIXC78mo7rmBy3YGiQ2M3Zvyq9vOPxhQhYSwRexFXOhUt0 21 | X2SYVCOE2MeGIAXt8jS3IZUCgYBO/cR4AHag9BUJWBwJlbBVuVI1gCniYdK36LXk 22 | oCb12xWcJ0j/VYNJdSRKbzLqI1zgeXIUzx3yMjTZx9dzCIvJgLRI1A3z/QnubFBR 23 | p0Zum179W2hrx0RDwznD2Vj0GQNYAb/I004O+2u+Xz+yZxGhTDzXl1V9mLHS39RQ 24 | tadNYQKBgQC+R83Yc4DFHluXa+k7jOIi5Zm9FwlLJZnIV9uZtAgMlO/o7BFBTQWx 25 | FgQBWR50cPxgzX9Jm8FvRUypAM4kvQtIM56+yVCxyk3J8Djv/VsF27a1Qer9s7TM 26 | hyqFfoWmTRQfYvWOsLxs4vcZABBaiidwTEMkod5rkoTHEgyDxAVJUA== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/fixtures/testthirdpartyattribute.txt: -------------------------------------------------------------------------------- 1 | ChFzb21lSXNzdWFuY2VUb2tlbhIvChgyMDE5LTEwLTE1VDIyOjA0OjA1LjEyM1oSEwoRY29tLnRoaXJkcGFydHkuaWQ= -------------------------------------------------------------------------------- /yoti_python_sdk/tests/image_helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from yoti_python_sdk.image import Image 3 | 4 | 5 | def assert_is_expected_image(image, image_type, expected_base64_last10): 6 | assert isinstance(image, Image) 7 | assert image.content_type == image_type 8 | assert image.mime_type() == "image/" + image_type 9 | assert image.base64_content()[-10:] == expected_base64_last10 10 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/protobuf_attribute.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from yoti_python_sdk.protobuf.protobuf import Protobuf 3 | 4 | 5 | class ProtobufAttribute(object): 6 | name = "" 7 | value = "" 8 | anchors = "" 9 | content_type = Protobuf.CT_UNDEFINED 10 | 11 | def __init__(self, name, value, anchors, content_type): 12 | self.name = name 13 | self.value = value 14 | self.anchors = anchors 15 | self.content_type = content_type 16 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/share/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getyoti/yoti-python-sdk/6fe15da5a2f997a8c503d73f9aa145bc79ecc6a7/yoti_python_sdk/tests/share/__init__.py -------------------------------------------------------------------------------- /yoti_python_sdk/tests/share/fixtures/testextradata.txt: -------------------------------------------------------------------------------- 1 | CmMIBhJfChFzb21lSXNzdWFuY2VUb2tlbhJKChgyMDE5LTEwLTE1VDIyOjA0OjA1LjEyM1oSEwoRY29tLnRoaXJkcGFydHkuaWQSGQoXY29tLnRoaXJkcGFydHkub3RoZXJfaWQ= -------------------------------------------------------------------------------- /yoti_python_sdk/tests/test_age_verification.py: -------------------------------------------------------------------------------- 1 | from yoti_python_sdk.attribute import Attribute 2 | from yoti_python_sdk.age_verification import AgeVerification 3 | from yoti_python_sdk.exceptions import MalformedAgeVerificationException 4 | from yoti_python_sdk import config 5 | import pytest 6 | 7 | 8 | @pytest.fixture(scope="module") 9 | def age_over_attribute(): 10 | return Attribute(config.ATTRIBUTE_AGE_OVER + "18", "true", None) 11 | 12 | 13 | @pytest.fixture(scope="module") 14 | def age_under_attribute(): 15 | return Attribute(config.ATTRIBUTE_AGE_UNDER + "18", "false", None) 16 | 17 | 18 | @pytest.mark.parametrize( 19 | "age_verification_name", 20 | [":age_over:18", "age_over:18:", "ageover:18", "age_over:", "age_over::18"], 21 | ) 22 | def test_malformed_age_verification_attributes(age_verification_name): 23 | with pytest.raises(MalformedAgeVerificationException): 24 | attribute = Attribute(age_verification_name, "true", None) 25 | age_verification_name = AgeVerification(attribute) 26 | 27 | 28 | def test_create_age_verification_from_age_over_attribute(age_over_attribute): 29 | age_verification = AgeVerification(age_over_attribute) 30 | 31 | assert age_verification.age == 18 32 | assert age_verification.check_type in config.ATTRIBUTE_AGE_OVER 33 | assert age_verification.result is True 34 | 35 | 36 | def test_create_age_verification_from_age_under_attribute(age_under_attribute): 37 | age_verification = AgeVerification(age_under_attribute) 38 | 39 | assert age_verification.age == 18 40 | assert age_verification.check_type in config.ATTRIBUTE_AGE_UNDER 41 | assert age_verification.result is False 42 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/test_aml.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import pytest 4 | 5 | from yoti_python_sdk import aml 6 | 7 | VALID_RESPONSE = '{"on_fraud_list":false,"on_pep_list":false,"on_watch_list":true}' 8 | INVALID_FORMAT_RESPONSE = json.loads( 9 | '{"on_fraud_list":false,"on_pep_list":false,"on_watch_list":true}' 10 | ) 11 | MISSING_FRAUD_LIST_RESPONSE = '{"on_pep_list":false,"on_watch_list":true}' 12 | VALID_AML_ADDRESS = aml.AmlAddress(country="FRA", postcode="ABC123") 13 | 14 | 15 | def test_getting_aml_result_with_valid_response(): 16 | aml.AmlResult(VALID_RESPONSE) 17 | 18 | 19 | def test_getting_aml_result_with_invalid_format_response(): 20 | with pytest.raises(RuntimeError) as exc: 21 | aml.AmlResult(INVALID_FORMAT_RESPONSE) 22 | expected_error = "Could not parse AML result from response" 23 | assert expected_error in str(exc.value) 24 | 25 | 26 | def test_getting_aml_result_with_missing_value(): 27 | with pytest.raises(TypeError): 28 | aml.AmlResult(MISSING_FRAUD_LIST_RESPONSE) 29 | 30 | 31 | def test_getting_aml_result_with_empty_string_response(): 32 | with pytest.raises(ValueError): 33 | aml.AmlResult("") 34 | 35 | 36 | def test_getting_aml_result_with_none_value(): 37 | with pytest.raises(ValueError): 38 | aml.AmlResult(None) 39 | 40 | 41 | def test_setting_aml_address_with_valid_values(): 42 | aml.AmlAddress(VALID_AML_ADDRESS) 43 | 44 | 45 | def test_setting_aml_profile_with_valid_values(): 46 | aml.AmlProfile( 47 | given_names="Joe", family_name="Bloggs", address=VALID_AML_ADDRESS, ssn="123456" 48 | ) 49 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/test_attribute.py: -------------------------------------------------------------------------------- 1 | import yoti_python_sdk.attribute 2 | 3 | from yoti_python_sdk import config 4 | from yoti_python_sdk.tests import anchor_fixture_parser 5 | 6 | NAME = "name" 7 | VALUE = "value" 8 | 9 | 10 | def test_attribute_get_values(): 11 | parsed_anchors = [] 12 | 13 | attribute = yoti_python_sdk.attribute.Attribute(NAME, VALUE, parsed_anchors) 14 | 15 | assert attribute.name == NAME 16 | assert attribute.value == VALUE 17 | assert attribute.anchors == parsed_anchors 18 | 19 | 20 | def test_attribute_get_sources(): 21 | anchors = create_source_and_verifier_anchors() 22 | attribute = yoti_python_sdk.attribute.Attribute(NAME, VALUE, anchors) 23 | sources = attribute.sources 24 | 25 | assert len(sources) == 1 26 | assert sources[0].anchor_type == config.ANCHOR_SOURCE 27 | 28 | 29 | def test_attribute_get_verifiers(): 30 | anchors = create_source_and_verifier_anchors() 31 | attribute = yoti_python_sdk.attribute.Attribute(NAME, VALUE, anchors) 32 | verifiers = attribute.verifiers 33 | 34 | assert len(verifiers) == 1 35 | assert verifiers[0].anchor_type == config.ANCHOR_VERIFIER 36 | 37 | 38 | def create_source_and_verifier_anchors(): 39 | passport_anchor = anchor_fixture_parser.get_parsed_passport_anchor() # source 40 | yoti_admin_anchor = anchor_fixture_parser.get_parsed_yoti_admin_anchor() # verifier 41 | 42 | return [passport_anchor, yoti_admin_anchor] 43 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/test_attribute_issuance_details.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os.path 4 | from yoti_python_sdk.tests import file_helper 5 | from yoti_python_sdk.attribute_issuance_details import AttributeIssuanceDetails 6 | from yoti_python_sdk.protobuf.share_public_api import ThirdPartyAttribute_pb2 7 | from yoti_python_sdk.protobuf.share_public_api import IssuingAttributes_pb2 8 | from datetime import datetime 9 | import base64 10 | import pytest 11 | 12 | FIXTURES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures") 13 | THIRD_PARTY_ATTRIBUTE = os.path.join(FIXTURES_DIR, "testthirdpartyattribute.txt") 14 | 15 | 16 | def create_issuance_test_proto(issuance_token, expiry_date, *definitions): 17 | issuing_attributes = IssuingAttributes_pb2.IssuingAttributes() 18 | issuing_attributes.expiry_date = expiry_date 19 | for s in definitions: 20 | name = IssuingAttributes_pb2.Definition() 21 | name.name = s 22 | issuing_attributes.definitions.extend([name]) 23 | 24 | attribute = ThirdPartyAttribute_pb2.ThirdPartyAttribute() 25 | attribute.issuance_token = str(issuance_token).encode("utf-8") 26 | attribute.issuing_attributes.MergeFrom(issuing_attributes) 27 | return attribute 28 | 29 | 30 | def test_should_parse_third_party_attribute_correctly(): 31 | thirdparty_attribute_bytes = file_helper.get_file_bytes(THIRD_PARTY_ATTRIBUTE) 32 | 33 | proto = ThirdPartyAttribute_pb2.ThirdPartyAttribute() 34 | proto.MergeFromString(thirdparty_attribute_bytes) 35 | 36 | issuance_details = AttributeIssuanceDetails(proto) 37 | 38 | assert issuance_details.attributes[0].name == "com.thirdparty.id" 39 | assert issuance_details.token == base64.b64encode( 40 | "someIssuanceToken".encode("utf-8") 41 | ) 42 | assert issuance_details.expiry_date == datetime(2019, 10, 15, 22, 4, 5, 123000) 43 | 44 | 45 | @pytest.mark.parametrize( 46 | "expiry_date", ["2006-13-02T15:04:05.000Z", "", "2006-11-02T15:04:05"] 47 | ) 48 | def test_should_return_none_if_error_in_parsing_date(expiry_date): 49 | proto = create_issuance_test_proto("someToken", expiry_date) 50 | 51 | issuance_details = AttributeIssuanceDetails(proto) 52 | 53 | assert issuance_details.expiry_date is None 54 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/test_attribute_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import logging 3 | 4 | import pytest 5 | 6 | from yoti_python_sdk import attribute_parser 7 | from yoti_python_sdk.protobuf import protobuf 8 | 9 | STRING_VALUE = "123" 10 | BYTE_VALUE = str.encode(STRING_VALUE) 11 | INT_VALUE = int(STRING_VALUE) 12 | 13 | 14 | @pytest.fixture(scope="module") 15 | def proto(): 16 | return protobuf.Protobuf() 17 | 18 | 19 | @pytest.mark.parametrize( 20 | "content_type, expected_value", 21 | [ 22 | (protobuf.Protobuf.CT_STRING, STRING_VALUE), 23 | (protobuf.Protobuf.CT_DATE, STRING_VALUE), 24 | (protobuf.Protobuf.CT_INT, INT_VALUE), 25 | ], 26 | ) 27 | def test_attribute_parser_values_based_on_content_type(content_type, expected_value): 28 | result = attribute_parser.value_based_on_content_type(BYTE_VALUE, content_type) 29 | assert result == expected_value 30 | 31 | 32 | def test_attribute_parser_values_based_on_other_content_types(proto): 33 | # disable logging for the below types: warning shown as type is not recognized 34 | logger = logging.getLogger() 35 | logger.propagate = False 36 | 37 | result = attribute_parser.value_based_on_content_type( 38 | BYTE_VALUE, proto.CT_UNDEFINED 39 | ) 40 | assert result == STRING_VALUE 41 | 42 | result = attribute_parser.value_based_on_content_type(BYTE_VALUE) 43 | assert result == STRING_VALUE 44 | 45 | result = attribute_parser.value_based_on_content_type(BYTE_VALUE, 100) 46 | assert result == STRING_VALUE 47 | 48 | logger.propagate = True 49 | 50 | 51 | def test_png_image_value_based_on_content_type(proto): 52 | result = attribute_parser.value_based_on_content_type(BYTE_VALUE, proto.CT_PNG) 53 | assert result.data == BYTE_VALUE 54 | assert result.content_type == "png" 55 | 56 | 57 | def test_jpeg_image_value_based_on_content_type(proto): 58 | result = attribute_parser.value_based_on_content_type(BYTE_VALUE, proto.CT_JPEG) 59 | assert result.data == BYTE_VALUE 60 | assert result.content_type == "jpeg" 61 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/test_crypto.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import pytest 5 | 6 | from yoti_python_sdk.crypto import Crypto 7 | 8 | 9 | @pytest.mark.parametrize("invalid_token", ["", None, "some_invalid_token", True, 123]) 10 | def test_decrypting_an_invalid_toke__should_not_be_allowed(invalid_token, crypto): 11 | with pytest.raises(ValueError): 12 | crypto.decrypt_token(invalid_token) 13 | 14 | 15 | def test_given_proper_encrypted_token__decrypting_should_yield_decrypted_token( 16 | encrypted_request_token, decrypted_request_token, crypto 17 | ): 18 | expected_token = decrypted_request_token 19 | decrypted_token = crypto.decrypt_token(encrypted_request_token).decode("utf-8") 20 | assert decrypted_token == expected_token 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "with_padding,stripped", 25 | [ 26 | (b"\xfa\x01", b"\xfa"), 27 | (b"\xfa\x06\x06\x06\x06\x06\x06", b"\xfa"), 28 | (b"\xfa\x08\x08\x08\x08\x08\x08\x08\x08", b"\xfa"), 29 | ], 30 | ) 31 | def test_strip_pkcs5_padding(with_padding, stripped): 32 | assert Crypto.strip_pkcs5_padding(with_padding) == stripped 33 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/test_date_parser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from datetime import datetime 5 | from yoti_python_sdk import date_parser 6 | import pytest 7 | 8 | 9 | @pytest.mark.parametrize( 10 | "string,expected", 11 | [ 12 | ("2006-11-02T15:04:05.010Z", datetime(2006, 11, 2, 15, 4, 5, 10000)), 13 | ("2006-09-02T15:04:05.010Z", datetime(2006, 9, 2, 15, 4, 5, 10000)), 14 | ("2006-9-02T15:04:05.010Z", datetime(2006, 9, 2, 15, 4, 5, 10000)), 15 | ("200006-11-02T15:04:05.010Z", None), 16 | ("2006-13-02T15:04:05.010Z", None), 17 | ("2006-09-31T15:04:05.010Z", None), 18 | ("2006-11-02T15:04:05", None), 19 | ("2006-11-02T15:04", None), 20 | ("2006-11-02T15", None), 21 | ("2006-11-02", None), 22 | ("2006-11", None), 23 | ("2006", None), 24 | ("This is not a date", None), 25 | ], 26 | ) 27 | def test_datetime_with_microsecond_should_handle_missing_and_invalid(string, expected): 28 | output = date_parser.datetime_with_microsecond(string) 29 | assert output == expected 30 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/test_image.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pytest 3 | 4 | from yoti_python_sdk.image import Image 5 | from yoti_python_sdk.protobuf.protobuf import Protobuf 6 | 7 | 8 | def test_image_with_unsupported_type(): 9 | with pytest.raises(TypeError): 10 | Image(b"", Protobuf.CT_UNDEFINED) 11 | 12 | 13 | @pytest.mark.parametrize( 14 | "content_type, expected_mime_type", 15 | [(Protobuf.CT_JPEG, "image/jpeg"), (Protobuf.CT_PNG, "image/png")], 16 | ) 17 | def test_image_mime_type(content_type, expected_mime_type): 18 | image = Image(b"", content_type) 19 | 20 | assert image.mime_type() == expected_mime_type 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "content_type, expected_base64_content", 25 | [ 26 | (Protobuf.CT_JPEG, "data:image/jpeg;base64,dGVzdCBzdHJpbmc="), 27 | (Protobuf.CT_PNG, "data:image/png;base64,dGVzdCBzdHJpbmc="), 28 | ], 29 | ) 30 | def test_image_base64_content(content_type, expected_base64_content): 31 | image = Image(b"test string", content_type) 32 | assert image.base64_content() == expected_base64_content 33 | -------------------------------------------------------------------------------- /yoti_python_sdk/tests/test_multivalue.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from yoti_python_sdk import multivalue 3 | from yoti_python_sdk.image import Image 4 | from yoti_python_sdk.tests import attribute_fixture_parser, image_helper 5 | 6 | 7 | def test_multi_value_filter_image(): 8 | parsed_images = attribute_fixture_parser.parse_multi_value() 9 | multi_value = [parsed_images[0], parsed_images[1], 0, "string_value"] 10 | 11 | assert len(multi_value) == 4 12 | 13 | filtered_tuple = multivalue.filter_values(multi_value, Image) 14 | 15 | assert len(filtered_tuple) == 2 16 | 17 | image_helper.assert_is_expected_image(filtered_tuple[0], "jpeg", "vWgD//2Q==") 18 | image_helper.assert_is_expected_image(filtered_tuple[1], "jpeg", "38TVEH/9k=") 19 | 20 | 21 | def test_multi_value_filter_int(): 22 | int_value = 0 23 | multi_value = [int_value, "string_value"] 24 | 25 | assert len(multi_value) == 2 26 | 27 | filtered_tuple = multivalue.filter_values(multi_value, int) 28 | 29 | assert len(filtered_tuple) == 1 30 | assert filtered_tuple[0] is int_value 31 | 32 | 33 | def test_multi_value_filter_string(): 34 | string_value = "string_value" 35 | multi_value = [0, string_value] 36 | 37 | assert len(multi_value) == 2 38 | 39 | filtered_tuple = multivalue.filter_values(multi_value, str) 40 | 41 | assert len(filtered_tuple) == 1 42 | assert filtered_tuple[0] is string_value 43 | 44 | 45 | def test_multi_value_filter_is_immutable(): 46 | original_string_value = "string_value" 47 | multi_value = [0, original_string_value] 48 | 49 | filtered_tuple = multivalue.filter_values(multi_value, str) 50 | 51 | filtered_tuple[0] == "changed_string_value" 52 | 53 | assert filtered_tuple[0] == original_string_value 54 | -------------------------------------------------------------------------------- /yoti_python_sdk/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | import uuid 3 | from abc import ABCMeta 4 | from abc import abstractmethod 5 | from json import JSONEncoder 6 | 7 | 8 | class YotiSerializable(object): 9 | """ 10 | Used to describe a class that is serializable by :class:`YotiEncoder`. 11 | """ 12 | 13 | __metaclass__ = ABCMeta 14 | 15 | @abstractmethod 16 | def to_json(self): 17 | raise NotImplementedError 18 | 19 | 20 | class YotiEncoder(JSONEncoder): 21 | def default(self, o): 22 | if isinstance(o, YotiSerializable): 23 | return o.to_json() 24 | return JSONEncoder.default(self, o) 25 | 26 | 27 | def create_nonce(): 28 | """ 29 | Create and return a nonce 30 | \ 31 | :return: the nonce 32 | """ 33 | return uuid.uuid4() 34 | 35 | 36 | def create_timestamp(): 37 | """ 38 | Create and return a timestamp 39 | 40 | :return: the timestamp as a int 41 | """ 42 | return int(time.time() * 1000) 43 | 44 | 45 | def remove_null_values(d): 46 | """ 47 | Delete keys with the value ``None`` in a dictionary, recursively. (None serializes to null) 48 | """ 49 | for key, value in list(d.items()): 50 | if value is None: 51 | del d[key] 52 | elif isinstance(value, dict): 53 | remove_null_values(value) 54 | return d 55 | -------------------------------------------------------------------------------- /yoti_python_sdk/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = "2.14.2" 3 | --------------------------------------------------------------------------------