diff --git a/.github/workflows/sonar.yaml b/.github/workflows/sonar.yaml
index d0a30566..f5055259 100644
--- a/.github/workflows/sonar.yaml
+++ b/.github/workflows/sonar.yaml
@@ -26,7 +26,7 @@ jobs:
- run: pytest --cov=yoti_python_sdk yoti_python_sdk/tests --cov-report=xml:coverage-reports/coverage-new.xml
- - run: sed -i 's+.*+/home/travis/build/getyoti/yoti-python-sdk/yoti_python_sdk+g' coverage-reports/coverage-new.xml
+ - run: sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace/@g' coverage-reports/coverage-new.xml
- uses: sonarsource/sonarcloud-github-action@master
env:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index ff260478..9506b63c 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -13,12 +13,12 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: [2.7, 3.6, 3.7, 3.8, 3.9, "3.10-dev"]
+ python-version: [3.7, 3.8, 3.9, "3.10"]
steps:
- uses: actions/checkout@v2
- - uses: actions/setup-python@v2.1.4
+ - uses: actions/setup-python@v2.3.1
with:
python-version: ${{ matrix.python-version }}
@@ -40,14 +40,16 @@ jobs:
steps:
- uses: actions/checkout@v2
- - uses: actions/setup-python@v2.1.4
+ - uses: actions/setup-python@v2.3.1
+ with:
+ python-version: 3.9
- - run: pip install -U setuptools==45
+ - run: pip install --upgrade setuptools
- run: pushd examples/aml && pip install -r requirements.txt && popd
- - run: pushd examples/yoti_example_django && pip install -r requirements.txt && popd
-
+ - run: pushd examples/yoti_example_django && pip install --upgrade pip && pip install -r requirements.txt && popd
+
- run: pushd examples/yoti_example_flask && pip install -r requirements.txt && popd
-
+
- run: pushd examples/doc_scan && pip install -r requirements.txt && popd
diff --git a/.gitignore b/.gitignore
index 8a197872..022b5cb0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -105,3 +105,4 @@ examples/yoti_example_django/*.pem
examples/yoti_example_flask/*.pem
.scannerwork
+.venv/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6ed7baf9..c94a38e6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,12 +1,13 @@
exclude: protobuf/
repos:
- repo: https://github.com/ambv/black
- rev: stable
+ rev: 22.3.0
hooks:
- id: black
- - repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v1.2.3
+
+ - repo: https://github.com/PyCQA/flake8
+ rev: 4.0.1
hooks:
- id: flake8
args:
- - --ignore=E501,W5
+ - --ignore=E501,W5
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 180a9b1f..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,77 +0,0 @@
-language: python
-
-dist: xenial
-
-git:
- depth: 3
-
-jobs:
- allow_failures:
- - python: "3.8-dev"
- include:
- - &test
- stage: Test
- python: "2.7"
- cache: pip
- before_install:
- - pip install -U setuptools
- install:
- - pip install -r requirements.txt
- - pip install -e .[dev]
- script:
- - pytest -v
- - <<: *test
- python: "3.4"
- - <<: *test
- python: "3.5"
- - <<: *test
- python: "3.5-dev"
- - <<: *test
- python: "3.6"
- - <<: *test
- python: "3.6-dev"
- - <<: *test
- python: "3.7"
- - <<: *test
- python: "3.7-dev"
- - <<: *test
- python: "3.8-dev"
- - <<: *test
- python: "3.8"
-
- - stage: Check Examples
- name: AML
- python: "3.8"
- script:
- - cd ./examples/aml
- - pip install -r requirements.txt
- - name: Django
- python: "3.8"
- script:
- - cd ./examples/yoti_example_django
- - pip install -r requirements.txt
- - name: Flask
- python: "3.8"
- script:
- - cd ./examples/yoti_example_flask
- - pip install -r requirements.txt
- - name: Doc Scan
- python: "3.8"
- script:
- - cd ./examples/doc_scan
- - pip install -r requirements.txt
-
- - stage: Analyze
- name: Sonarcloud
- python: "3.8"
- addons:
- sonarcloud:
- organization: "getyoti"
- install:
- - pip install -r requirements.txt
- - pip install -e .[dev]
- script:
- - pytest --cov=yoti_python_sdk yoti_python_sdk/tests --cov-report=xml:coverage-reports/coverage-new.xml
- - sed -i 's+.*+/home/travis/build/getyoti/yoti-python-sdk/yoti_python_sdk+g' coverage-reports/coverage-new.xml
- - sonar-scanner
- if: type = pull_request OR branch = master
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 86e2f477..51e32699 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,7 +7,7 @@
## Adding Features
Any features added must be fully tested and documented, with examples supplied in the pull request.
-The feature must support the lowest Python version that the SDK supports (see [the travis file](.travis.yml) for all supported versions). The feature
+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
must not introduce any unnecessary dependencies (although introducing a new third party library
is open for discussion if absolutely required).
diff --git a/README.md b/README.md
index 1014886c..d50a9736 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,7 @@ Yoti also allows you to enable user details verification from your mobile app by
## Requirements
-To see the versions of Python this SDK is compatible with, see the [.travis.yml](/.travis.yml) file.
+To see the versions of Python this SDK is compatible with, see the [the GitHub workflow tests file](./.github/workflows/tests.yaml) file.
## Installing the SDK
diff --git a/examples/aml/requirements.in b/examples/aml/requirements.in
new file mode 100644
index 00000000..7e25054c
--- /dev/null
+++ b/examples/aml/requirements.in
@@ -0,0 +1,2 @@
+yoti>=2.14.0
+python-dotenv>=0.7.1
diff --git a/examples/aml/requirements.txt b/examples/aml/requirements.txt
index b6c39efd..33889910 100644
--- a/examples/aml/requirements.txt
+++ b/examples/aml/requirements.txt
@@ -1,2 +1,46 @@
-yoti>=2.13.0
-python-dotenv>=0.7.1
+#
+# This file is autogenerated by pip-compile with python 3.10
+# To update, run:
+#
+# pip-compile --output-file=requirements.txt requirements.in
+#
+asn1==2.2.0
+ # via yoti
+certifi==2021.10.8
+ # via requests
+cffi==1.15.0
+ # via cryptography
+charset-normalizer==2.0.10
+ # via requests
+cryptography==36.0.1
+ # via
+ # pyopenssl
+ # yoti
+deprecated==1.2.10
+ # via yoti
+future==0.18.2
+ # via yoti
+idna==3.3
+ # via requests
+iso8601==0.1.13
+ # via yoti
+protobuf==3.19.3
+ # via yoti
+pycparser==2.21
+ # via cffi
+pyopenssl==21.0.0
+ # via yoti
+python-dotenv==0.19.2
+ # via -r requirements.in
+pytz==2020.4
+ # via yoti
+requests==2.27.1
+ # via yoti
+six==1.16.0
+ # via pyopenssl
+urllib3==1.26.8
+ # via requests
+wrapt==1.13.3
+ # via deprecated
+yoti==2.14.0
+ # via -r requirements.in
diff --git a/examples/doc_scan/app.py b/examples/doc_scan/app.py
index 3de18b4d..d550b6a6 100644
--- a/examples/doc_scan/app.py
+++ b/examples/doc_scan/app.py
@@ -47,6 +47,7 @@ def create_session():
.with_preset_issuing_country("GBR")
.with_success_url("{url}/success".format(url=YOTI_APP_BASE_URL))
.with_error_url("{url}/error".format(url=YOTI_APP_BASE_URL))
+ .with_privacy_policy_url("{url}/privacy-policy".format(url=YOTI_APP_BASE_URL))
.build()
)
@@ -173,5 +174,10 @@ def media():
)
+@app.route("/privacy-policy")
+def privacy_policy():
+ return render_template("privacy.html")
+
+
if __name__ == "__main__":
app.run()
diff --git a/examples/doc_scan/requirements.in b/examples/doc_scan/requirements.in
index 4bfbe87a..572ae2b7 100644
--- a/examples/doc_scan/requirements.in
+++ b/examples/doc_scan/requirements.in
@@ -1,5 +1,6 @@
flask>=1.1.2
python-dotenv>=0.13.0
-yoti>=2.13.0
+yoti>=2.14.0
filetype>=1.0.7
pyopenssl>=19.1.0
+six>=1.16.0
\ No newline at end of file
diff --git a/examples/doc_scan/requirements.txt b/examples/doc_scan/requirements.txt
index 38b136a4..f0dca95c 100644
--- a/examples/doc_scan/requirements.txt
+++ b/examples/doc_scan/requirements.txt
@@ -1,34 +1,69 @@
#
-# This file is autogenerated by pip-compile
+# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
# pip-compile --output-file=requirements.txt requirements.in
#
-asn1==2.2.0 # via yoti
-certifi==2020.4.5.1 # via requests
-cffi==1.14.0 # via cryptography
-chardet==3.0.4 # via requests
-click==7.1.2 # via flask
-cryptography==3.2 # via pyopenssl, yoti
-deprecated==1.2.10 # via yoti
-filetype==1.0.7 # via -r requirements.in
-flask==1.1.2 # via -r requirements.in
-future==0.18.2 # via yoti
-idna==2.9 # via requests
-iso8601==0.1.13 # via yoti
-itsdangerous==1.1.0 # via flask
-jinja2==2.11.2 # via flask
-markupsafe==1.1.1 # via jinja2
-protobuf==3.11.3 # via yoti
-pycparser==2.20 # via cffi
-pyopenssl==19.1.0 # via -r requirements.in, yoti
-python-dotenv==0.13.0 # via -r requirements.in
-requests==2.23.0 # via yoti
-six==1.14.0 # via cryptography, protobuf, pyopenssl
-urllib3==1.25.9 # via requests
-werkzeug==1.0.1 # via flask
-wrapt==1.12.1 # via deprecated
-yoti==2.13.0 # via -r requirements.in
+asn1==2.2.0
+ # via yoti
+certifi==2020.4.5.1
+ # via requests
+cffi==1.14.0
+ # via cryptography
+chardet==3.0.4
+ # via requests
+click==7.1.2
+ # via flask
+cryptography==3.2
+ # via
+ # pyopenssl
+ # yoti
+deprecated==1.2.10
+ # via yoti
+filetype==1.0.7
+ # via -r requirements.in
+flask==1.1.2
+ # via -r requirements.in
+future==0.18.2
+ # via yoti
+idna==2.9
+ # via requests
+iso8601==0.1.13
+ # via yoti
+itsdangerous==1.1.0
+ # via flask
+jinja2==2.11.2
+ # via flask
+markupsafe==1.1.1
+ # via jinja2
+protobuf==3.11.3
+ # via yoti
+pycparser==2.20
+ # via cffi
+pyopenssl==19.1.0
+ # via
+ # -r requirements.in
+ # yoti
+python-dotenv==0.13.0
+ # via -r requirements.in
+pytz==2020.4
+ # via yoti
+requests==2.23.0
+ # via yoti
+six==1.16.0
+ # via
+ # -r requirements.in
+ # cryptography
+ # protobuf
+ # pyopenssl
+urllib3==1.25.9
+ # via requests
+werkzeug==1.0.1
+ # via flask
+wrapt==1.12.1
+ # via deprecated
+yoti==2.14.0
+ # via -r requirements.in
# The following packages are considered to be unsafe in a requirements file:
# setuptools
diff --git a/examples/doc_scan/templates/privacy.html b/examples/doc_scan/templates/privacy.html
new file mode 100644
index 00000000..f3b4c54a
--- /dev/null
+++ b/examples/doc_scan/templates/privacy.html
@@ -0,0 +1,10 @@
+{% include "layout/header.html" %}
+
+
+
+
Privacy Policy
+
Demo privacy policy
+
+
+
+{% include "layout/footer.html" %}
\ No newline at end of file
diff --git a/examples/yoti_example_django/Dockerfile b/examples/yoti_example_django/Dockerfile
index 50c2bf9b..0eed957f 100644
--- a/examples/yoti_example_django/Dockerfile
+++ b/examples/yoti_example_django/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.7-slim
+FROM python:3.9-slim
ARG YOTI_SCENARIO_ID
ARG YOTI_CLIENT_SDK_ID
ARG YOTI_KEY_FILE_PATH
diff --git a/examples/yoti_example_django/requirements.in b/examples/yoti_example_django/requirements.in
index 6210c53b..884d3bd9 100644
--- a/examples/yoti_example_django/requirements.in
+++ b/examples/yoti_example_django/requirements.in
@@ -1,6 +1,8 @@
-django>=3.0.7
+django>=4.0.1
django-sslserver>=0.22.0
python-dotenv>=0.7.1
requests>=2.20.0
urllib3>=1.24.2
-yoti>=2.13.0
+yoti>=2.14.0
+six>=1.16.0
+cffi>=1.15.0
\ No newline at end of file
diff --git a/examples/yoti_example_django/requirements.txt b/examples/yoti_example_django/requirements.txt
index 743d3e65..d966b31c 100644
--- a/examples/yoti_example_django/requirements.txt
+++ b/examples/yoti_example_django/requirements.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile
+# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
# pip-compile --output-file=requirements.txt requirements.in
@@ -8,10 +8,14 @@ asgiref==3.4.1
# via django
asn1==2.2.0
# via yoti
+backports.zoneinfo==0.2.1
+ # via django
certifi==2018.4.16
# via requests
-cffi==1.14.0
- # via cryptography
+cffi==1.15.0
+ # via
+ # -r requirements.in
+ # cryptography
chardet==3.0.4
# via requests
cryptography==3.2
@@ -20,7 +24,7 @@ cryptography==3.2
# yoti
deprecated==1.2.10
# via yoti
-django==3.1.12
+django==4.0.1
# via
# -r requirements.in
# django-sslserver
@@ -40,14 +44,15 @@ pyopenssl==18.0.0
# via yoti
python-dotenv==0.8.2
# via -r requirements.in
-pytz==2018.4
- # via django
+pytz==2020.4
+ # via yoti
requests==2.21.0
# via
# -r requirements.in
# yoti
-six==1.11.0
+six==1.16.0
# via
+ # -r requirements.in
# cryptography
# protobuf
# pyopenssl
@@ -59,7 +64,7 @@ urllib3==1.24.2
# requests
wrapt==1.12.1
# via deprecated
-yoti==2.13.0
+yoti==2.14.0
# via -r requirements.in
# The following packages are considered to be unsafe in a requirements file:
diff --git a/examples/yoti_example_django/yoti_example/urls.py b/examples/yoti_example_django/yoti_example/urls.py
index f8063333..77688930 100644
--- a/examples/yoti_example_django/yoti_example/urls.py
+++ b/examples/yoti_example_django/yoti_example/urls.py
@@ -13,17 +13,17 @@
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
-from django.conf.urls import url
+from django.urls import re_path
from django.contrib import admin
from .views import IndexView, AuthView, DynamicShareView, SourceConstraintsView
urlpatterns = [
- url(r"^$", IndexView.as_view(), name="index"),
- url(r"^yoti/auth/$", AuthView.as_view(), name="auth"),
- url(r"^admin/", admin.site.urls),
- url(r"^dynamic-share/$", DynamicShareView.as_view(), name="dynamic-share"),
- url(
+ re_path(r"^$", IndexView.as_view(), name="index"),
+ re_path(r"^yoti/auth/$", AuthView.as_view(), name="auth"),
+ re_path(r"^admin/", admin.site.urls),
+ re_path(r"^dynamic-share/$", DynamicShareView.as_view(), name="dynamic-share"),
+ re_path(
r"^source-constraint/$",
SourceConstraintsView.as_view(),
name="source-constraints",
diff --git a/examples/yoti_example_flask/Dockerfile b/examples/yoti_example_flask/Dockerfile
index a590e43a..4ec911ef 100644
--- a/examples/yoti_example_flask/Dockerfile
+++ b/examples/yoti_example_flask/Dockerfile
@@ -1,4 +1,4 @@
-FROM python:3.6.3
+FROM python:3.9.12
ARG YOTI_SCENARIO_ID
ARG YOTI_CLIENT_SDK_ID
ARG YOTI_KEY_FILE_PATH
diff --git a/examples/yoti_example_flask/README.md b/examples/yoti_example_flask/README.md
index 2589ede5..b63600f1 100644
--- a/examples/yoti_example_flask/README.md
+++ b/examples/yoti_example_flask/README.md
@@ -2,5 +2,5 @@
1. Rename the [.env.example](.env.example) file to `.env` and fill in the required configuration values
1. Install dependencies: `pip install -r requirements.txt`
-1. Run `python app.py`
+1. Run `python -m flask run`
1. Navigate to https://localhost:5000
\ No newline at end of file
diff --git a/examples/yoti_example_flask/requirements.in b/examples/yoti_example_flask/requirements.in
index 89c202b6..89d0b0ee 100644
--- a/examples/yoti_example_flask/requirements.in
+++ b/examples/yoti_example_flask/requirements.in
@@ -1,9 +1,11 @@
-cffi>=1.14.0
+click>=7
+cffi>=1.15.0
flask>=1.0.4
-jinja2>=2.8.1
+jinja2>=3.0.3
pyopenssl>=19.0.0
python-dotenv>=0.7.1
requests>=2.20.0
urllib3>=1.24.2
-yoti>=2.13.0
+yoti>=2.14.0
werkzeug>=1.0.1
+six==1.16.0
\ No newline at end of file
diff --git a/examples/yoti_example_flask/requirements.txt b/examples/yoti_example_flask/requirements.txt
index a5a293a8..81f2e9f6 100644
--- a/examples/yoti_example_flask/requirements.txt
+++ b/examples/yoti_example_flask/requirements.txt
@@ -1,33 +1,79 @@
#
-# This file is autogenerated by pip-compile
+# This file is autogenerated by pip-compile with python 3.9
# To update, run:
#
# pip-compile --output-file=requirements.txt requirements.in
#
-asn1==2.2.0 # via yoti
-certifi==2018.4.16 # via requests
-cffi==1.14.0 # via -r requirements.in, cryptography
-chardet==3.0.4 # via requests
-click==6.7 # via flask
-cryptography==3.2 # via pyopenssl, yoti
-deprecated==1.2.10 # via yoti
-flask==1.1.1 # via -r requirements.in
-future==0.16.0 # via yoti
-idna==2.7 # via requests
-iso8601==0.1.13 # via yoti
-itsdangerous==0.24 # via flask
-jinja2==2.10.1 # via -r requirements.in, flask
-markupsafe==1.0 # via jinja2
-protobuf==3.6.0 # via yoti
-pycparser==2.18 # via cffi
-pyopenssl==19.0.0 # via -r requirements.in, yoti
-python-dotenv==0.8.2 # via -r requirements.in
-requests==2.21.0 # via -r requirements.in, yoti
-six==1.11.0 # via cryptography, protobuf, pyopenssl
-urllib3==1.24.2 # via -r requirements.in, requests
-werkzeug==1.0.1 # via -r requirements.in, flask
-wrapt==1.12.1 # via deprecated
-yoti==2.13.0 # via -r requirements.in
+asn1==2.2.0
+ # via yoti
+certifi==2018.4.16
+ # via requests
+cffi==1.15.0
+ # via
+ # -r requirements.in
+ # cryptography
+chardet==3.0.4
+ # via requests
+click==8.1.2
+ # via
+ # -r requirements.in
+ # flask
+cryptography==3.2
+ # via
+ # pyopenssl
+ # yoti
+deprecated==1.2.10
+ # via yoti
+flask==1.1.1
+ # via -r requirements.in
+future==0.16.0
+ # via yoti
+idna==2.7
+ # via requests
+iso8601==0.1.13
+ # via yoti
+itsdangerous==0.24
+ # via flask
+jinja2==3.0.3
+ # via
+ # -r requirements.in
+ # flask
+markupsafe==2.0.1
+ # via jinja2
+protobuf==3.6.0
+ # via yoti
+pycparser==2.18
+ # via cffi
+pyopenssl==19.0.0
+ # via
+ # -r requirements.in
+ # yoti
+python-dotenv==0.8.2
+ # via -r requirements.in
+pytz==2020.4
+ # via yoti
+requests==2.21.0
+ # via
+ # -r requirements.in
+ # yoti
+six==1.16.0
+ # via
+ # -r requirements.in
+ # cryptography
+ # protobuf
+ # pyopenssl
+urllib3==1.24.2
+ # via
+ # -r requirements.in
+ # requests
+werkzeug==1.0.1
+ # via
+ # -r requirements.in
+ # flask
+wrapt==1.12.1
+ # via deprecated
+yoti==2.14.0
+ # via -r requirements.in
# The following packages are considered to be unsafe in a requirements file:
# setuptools
diff --git a/requirements.in b/requirements.in
index a229f183..e3096e0a 100644
--- a/requirements.in
+++ b/requirements.in
@@ -1,15 +1,16 @@
asn1==2.2.0 # asn1 2.3.0 introduces enum34 as a dependency, which causes problems on some envs
cryptography==2.8.0
-cffi==1.14.3
+cffi==1.15.0
future==0.18.2
-itsdangerous==1.1.0
+itsdangerous==2.0.1
pbr==1.10.0
-protobuf==3.13.0
+protobuf==3.19.4
pyopenssl==19.1.0
PyYAML==5.2 # PyYAML 5.3 does not support Python 3.4
-pytz==2020.4
+pytz==2022.1
requests>=2.20.0
urllib3>=1.24.3
-deprecated==1.2.10
-wheel==0.33.6
-iso8601==0.1.13
+deprecated==1.2.13
+wheel==0.37.1
+iso8601==1.0.2
+six>=1.16.0
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index b8c0490b..ea88377b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,30 +1,57 @@
#
-# This file is autogenerated by pip-compile
+# This file is autogenerated by pip-compile with python 3.9
# To update, run:
#
# pip-compile --output-file=requirements.txt requirements.in
#
-asn1==2.2.0 # via -r requirements.in
-certifi==2018.11.29 # via requests
-cffi==1.14.3 # via -r requirements.in, cryptography
-chardet==3.0.4 # via requests
-cryptography==2.8 # via -r requirements.in, pyopenssl
-deprecated==1.2.10 # via -r requirements.in
-future==0.18.2 # via -r requirements.in
-idna==2.7 # via requests
-iso8601==0.1.13 # via -r requirements.in
-itsdangerous==1.1.0 # via -r requirements.in
-pbr==1.10.0 # via -r requirements.in
-protobuf==3.13.0 # via -r requirements.in
-pycparser==2.18 # via cffi
-pyopenssl==19.1.0 # via -r requirements.in
-pytz==2020.4 # via -r requirements.in
-pyyaml==5.2 # via -r requirements.in
-requests==2.21.0 # via -r requirements.in
-six==1.10.0 # via cryptography, protobuf, pyopenssl
-urllib3==1.24.3 # via -r requirements.in, requests
-wheel==0.33.6 # via -r requirements.in
-wrapt==1.11.2 # via deprecated
-
-# The following packages are considered to be unsafe in a requirements file:
-# setuptools
+asn1==2.2.0
+ # via -r requirements.in
+certifi==2018.11.29
+ # via requests
+cffi==1.15.0
+ # via
+ # -r requirements.in
+ # cryptography
+chardet==3.0.4
+ # via requests
+cryptography==2.8
+ # via
+ # -r requirements.in
+ # pyopenssl
+deprecated==1.2.13
+ # via -r requirements.in
+future==0.18.2
+ # via -r requirements.in
+idna==2.7
+ # via requests
+iso8601==1.0.2
+ # via -r requirements.in
+itsdangerous==2.0.1
+ # via -r requirements.in
+pbr==1.10.0
+ # via -r requirements.in
+protobuf==3.19.4
+ # via -r requirements.in
+pycparser==2.18
+ # via cffi
+pyopenssl==19.1.0
+ # via -r requirements.in
+pytz==2022.1
+ # via -r requirements.in
+pyyaml==5.2
+ # via -r requirements.in
+requests==2.21.0
+ # via -r requirements.in
+six==1.16.0
+ # via
+ # -r requirements.in
+ # cryptography
+ # pyopenssl
+urllib3==1.24.3
+ # via
+ # -r requirements.in
+ # requests
+wheel==0.37.1
+ # via -r requirements.in
+wrapt==1.11.2
+ # via deprecated
diff --git a/setup.py b/setup.py
index 4f3033a6..7fba8798 100644
--- a/setup.py
+++ b/setup.py
@@ -18,15 +18,16 @@
author="Yoti",
author_email="websdk@yoti.com",
install_requires=[
- "deprecated==1.2.10",
+ "deprecated==1.2.13",
"cryptography>=2.2.1",
"protobuf>=3.1.0",
"requests>=2.11.1",
- "future>=0.11.0",
+ "future>=0.18.2",
"asn1==2.2.0",
"pyopenssl>=18.0.0",
- "iso8601==0.1.13",
- "pytz==2020.4",
+ "iso8601==1.0.2",
+ "wheel==0.37.1",
+ "pytz==2021.3",
],
extras_require={
"examples": [
@@ -34,18 +35,20 @@
"Flask>=1.0.4",
"python-dotenv>=0.7.1",
"django-sslserver>=0.22.0",
- "Werkzeug==1.0.1",
+ "Werkzeug==2.0.2",
],
"dev": [
- "pre-commit==1.17.0",
- "pytest>=4.6.11",
+ "pre-commit==2.16.0",
+ "pytest>=7.1.2",
"pytest-cov>=2.7.1",
"pylint==1.9.4",
"pylint-exit>=1.1.0",
"python-coveralls==2.9.3",
"coverage==4.5.4",
"mock==2.0.0",
- "virtualenv==20.1.0",
+ "virtualenv==20.13.0",
+ "flake8==4.0.1",
+ "pip-tools==6.6.0",
],
},
classifiers=[
@@ -54,13 +57,12 @@
"Operating System :: OS Independent",
"Intended Audience :: Developers",
"Programming Language :: Python",
- "Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.4",
- "Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
"Topic :: Software Development :: Libraries :: Python Modules",
],
keywords="yoti sdk 2FA multifactor authentication verification identity login register verify 2Factor",
diff --git a/sonar-project.properties b/sonar-project.properties
index b8f41b51..f77622f8 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -2,7 +2,7 @@ sonar.host.url = https://sonarcloud.io
sonar.organization = getyoti
sonar.projectKey = getyoti:python
sonar.projectName = Python SDK
-sonar.projectVersion = 2.14.0
+sonar.projectVersion = 2.15.0
sonar.exclusions = yoti_python_sdk/tests/**,examples/**,yoti_python_sdk/protobuf/**/*
sonar.python.pylint.reportPath = coverage.out
diff --git a/yoti_python_sdk/__init__.py b/yoti_python_sdk/__init__.py
index a33747d5..e2084235 100644
--- a/yoti_python_sdk/__init__.py
+++ b/yoti_python_sdk/__init__.py
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
import os
-from distutils.util import convert_path
from os import environ
-
+from .version import __version__
from yoti_python_sdk.client import Client
DEFAULTS = {
@@ -17,11 +16,8 @@
directory_name = os.path.dirname(__file__)
version_path = os.path.join(directory_name, "version.py")
-ver_path = convert_path(version_path)
-with open(ver_path) as ver_file:
- exec(ver_file.read(), main_ns)
+exec(open(version_path).read())
-__version__ = main_ns["__version__"]
YOTI_API_URL = environ.get("YOTI_API_URL", DEFAULTS["YOTI_API_URL"])
YOTI_PROFILE_ENDPOINT = "/api/v1"
diff --git a/yoti_python_sdk/doc_scan/constants.py b/yoti_python_sdk/doc_scan/constants.py
index 6aa18d1d..279d4e5e 100644
--- a/yoti_python_sdk/doc_scan/constants.py
+++ b/yoti_python_sdk/doc_scan/constants.py
@@ -36,3 +36,5 @@
IGNORE = "IGNORE"
PROOF_OF_ADDRESS = "PROOF_OF_ADDRESS"
+
+WATCHLIST_SCREENING_CHECK_TYPE = "WATCHLIST_SCREENING"
diff --git a/yoti_python_sdk/doc_scan/session/create/check/__init__.py b/yoti_python_sdk/doc_scan/session/create/check/__init__.py
index c0c38292..89d5689f 100644
--- a/yoti_python_sdk/doc_scan/session/create/check/__init__.py
+++ b/yoti_python_sdk/doc_scan/session/create/check/__init__.py
@@ -2,10 +2,13 @@
from .document_comparison import RequestedIDDocumentComparisonCheckBuilder
from .face_match import RequestedFaceMatchCheckBuilder
from .liveness import RequestedLivenessCheckBuilder
+from .watchlist_screen import WatchlistScreeningCheckBuilder
+
__all__ = [
"RequestedDocumentAuthenticityCheckBuilder",
"RequestedIDDocumentComparisonCheckBuilder",
"RequestedFaceMatchCheckBuilder",
"RequestedLivenessCheckBuilder",
+ "WatchlistScreeningCheckBuilder",
]
diff --git a/yoti_python_sdk/doc_scan/session/create/check/watchlist_screen.py b/yoti_python_sdk/doc_scan/session/create/check/watchlist_screen.py
new file mode 100644
index 00000000..d686c816
--- /dev/null
+++ b/yoti_python_sdk/doc_scan/session/create/check/watchlist_screen.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from yoti_python_sdk.doc_scan import constants
+from yoti_python_sdk.utils import YotiSerializable, remove_null_values
+from .requested_check import RequestedCheck
+
+
+class WatchlistScreeningCheckConfig(YotiSerializable):
+ """
+ The configuration applied when creating a Watchlist screening check.
+ """
+
+ def __init__(self, manual_check, categories):
+ """
+ :param manual_check: the watchlist screening check manual_check eg. "NEVER"
+ :type type: str
+ :param categories: list of categories for watchlist screening check config
+ :type max_retries: list
+ """
+ self.__categories = categories
+ self.__manual_check = manual_check
+
+ @property
+ def manual_check(self):
+ """
+ Watchlist screening check manual check value
+
+ :return: str
+ """
+ return self.__manual_check
+
+ @property
+ def categories(self):
+ """
+ Watchlist screening check categories
+
+ :return: list
+ """
+ return self.__categories
+
+ def to_json(self):
+ return remove_null_values(
+ {"manual_check": self.manual_check, "categories": self.__categories}
+ )
+
+
+class WatchlistScreeningCheck(RequestedCheck):
+ """
+ Requests creation of a Watchlist screening check
+ """
+
+ def __init__(self, config):
+ """
+ :param config: the Watchlist screening check configuration
+ :type config: WatchlistScreeningCheckConfig
+ """
+ self.__config = config
+
+ @property
+ def type(self):
+ return constants.WATCHLIST_SCREENING_CHECK_TYPE
+
+ @property
+ def config(self):
+ return self.__config
+
+
+class WatchlistScreeningCheckBuilder(object):
+ """
+ Builder to assist creation of :class:`WatchlistScreeningCheck`
+ """
+
+ def __init__(self):
+ self.__categories = None
+ self.__manual_check = None
+
+ def with_categories(self, categories):
+ """
+ Sets the WatchListScreeningCheck categories
+
+ :return: the builder
+ :rtype: WatchlistScreeningCheckBuilder
+ """
+ self.__categories = categories
+
+ return self
+
+ def with_manual_check(self, manual_check):
+ """
+ Sets the WatchListScreeningCheck manual_check
+
+ :param liveness_type: the manual_check
+ :type liveness_type: str
+ :return: the builder
+ :rtype: WatchlistScreeningCheckBuilder
+ """
+ self.__manual_check = manual_check
+
+ return self
+
+ def build(self):
+ config = WatchlistScreeningCheckConfig(
+ self.__manual_check, self.__categories or []
+ )
+ return WatchlistScreeningCheck(config)
diff --git a/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py b/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py
index 6e0bcf4e..63d634ae 100644
--- a/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py
+++ b/yoti_python_sdk/doc_scan/session/create/filter/orthogonal_restrictions_filter.py
@@ -68,11 +68,14 @@ def to_json(self):
class OrthogonalRestrictionsFilter(DocumentFilter):
- def __init__(self, country_restriction, type_restriction):
+ def __init__(
+ self, country_restriction, type_restriction, allow_non_latin_documents=None
+ ):
DocumentFilter.__init__(self, filter_type=ORTHOGONAL_RESTRICTIONS)
self.__country_restriction = country_restriction
self.__type_restriction = type_restriction
+ self.__allow_non_latin_documents = allow_non_latin_documents
@property
def country_restriction(self):
@@ -94,10 +97,23 @@ def type_restriction(self):
"""
return self.__type_restriction
+ @property
+ def allow_non_latin_documents(self):
+ """
+ Returns the flag for whether non-latin documents are allowed.
+
+ :return: allow_non_latin_documents
+ :rtype: bool
+ """
+ return self.__allow_non_latin_documents
+
def to_json(self):
parent = DocumentFilter.to_json(self)
parent["country_restriction"] = self.country_restriction
parent["type_restriction"] = self.type_restriction
+ if self.__allow_non_latin_documents is not None:
+ parent["allow_non_latin_documents"] = self.__allow_non_latin_documents
+
return remove_null_values(parent)
@@ -117,6 +133,7 @@ class OrthogonalRestrictionsFilterBuilder(object):
def __init__(self):
self.__country_restriction = None
self.__type_restriction = None
+ self.__allow_non_latin_documents = None
def with_whitelisted_country_codes(self, country_codes):
"""
@@ -170,6 +187,26 @@ def with_blacklisted_document_types(self, document_types):
self.__type_restriction = TypeRestriction(INCLUSION_BLACKLIST, document_types)
return self
+ def allow_non_latin_documents(self):
+ """
+ Sets a True value for "allow non-latin documents" flag.
+
+ :return: the builder
+ :rtype: OrthogonalRestrictionsFilterBuilder
+ """
+ self.__allow_non_latin_documents = True
+ return self
+
+ def disable_non_latin_documents(self):
+ """
+ Sets a False value for "allow non-latin documents" flag.
+
+ :return: the builder
+ :rtype: OrthogonalRestrictionsFilterBuilder
+ """
+ self.__allow_non_latin_documents = False
+ return self
+
def build(self):
"""
Builds the orthogonal filter, using the supplied whitelisted/blacklisted values
@@ -178,5 +215,7 @@ def build(self):
:rtype: OrthogonalRestrictionsFilter
"""
return OrthogonalRestrictionsFilter(
- self.__country_restriction, self.__type_restriction
+ self.__country_restriction,
+ self.__type_restriction,
+ self.__allow_non_latin_documents,
)
diff --git a/yoti_python_sdk/doc_scan/session/create/notification_config.py b/yoti_python_sdk/doc_scan/session/create/notification_config.py
index 476a38b0..d89a5ce0 100644
--- a/yoti_python_sdk/doc_scan/session/create/notification_config.py
+++ b/yoti_python_sdk/doc_scan/session/create/notification_config.py
@@ -16,7 +16,7 @@ class NotificationConfig(YotiSerializable):
to poll for the state of the Session.
"""
- def __init__(self, auth_token, endpoint, topics=None):
+ def __init__(self, auth_token, endpoint, topics=None, auth_type=None):
"""
:param auth_token: the authorization token
:type auth_token: str
@@ -31,6 +31,7 @@ def __init__(self, auth_token, endpoint, topics=None):
self.__auth_token = auth_token
self.__endpoint = endpoint
self.__topics = list(set(topics)) # Get unique values
+ self.__auth_type = auth_type
@property
def auth_token(self):
@@ -62,9 +63,21 @@ def topics(self):
"""
return self.__topics
+ @property
+ def auth_type(self):
+ """
+ The authentication type that the notification will use to
+ authenticate itself.
+
+ :return: the endpoint
+ :rtype: str
+ """
+ return self.__auth_type
+
def to_json(self):
return remove_null_values(
{
+ "auth_type": self.auth_type,
"auth_token": self.auth_token,
"endpoint": self.endpoint,
"topics": self.topics,
@@ -81,6 +94,7 @@ def __init__(self):
self.__auth_token = None
self.__endpoint = None
self.__topics = []
+ self.__auth_type = None
def with_auth_token(self, token):
"""
@@ -154,6 +168,26 @@ def for_check_completion(self):
"""
return self.with_topic(CHECK_COMPLETION)
+ def with_basic_auth_type(self):
+ """
+ Setup "BASIC" auth type for notifications.
+
+ :return: the builder
+ :rtype: NotificationConfigBuilder
+ """
+ self.__auth_type = "BASIC"
+ return self
+
+ def with_bearer_auth_type(self):
+ """
+ Setup "BEARER" auth type for notifications.
+
+ :return: the builder
+ :rtype: NotificationConfigBuilder
+ """
+ self.__auth_type = "BEARER"
+ return self
+
def build(self):
"""
Builds the :class:`NotificationConfig` using the supplied values
@@ -161,4 +195,6 @@ def build(self):
:return: the build notification config
:rtype: NotificationConfig
"""
- return NotificationConfig(self.__auth_token, self.__endpoint, self.__topics)
+ return NotificationConfig(
+ self.__auth_token, self.__endpoint, self.__topics, self.__auth_type
+ )
diff --git a/yoti_python_sdk/doc_scan/session/create/sdk_config.py b/yoti_python_sdk/doc_scan/session/create/sdk_config.py
index c76fe897..37cde6b5 100644
--- a/yoti_python_sdk/doc_scan/session/create/sdk_config.py
+++ b/yoti_python_sdk/doc_scan/session/create/sdk_config.py
@@ -21,6 +21,8 @@ def __init__(
preset_issuing_country,
success_url,
error_url,
+ allow_handoff=None,
+ privacy_policy_url=None,
):
"""
:param allowed_capture_methods: the allowed capture methods
@@ -39,6 +41,10 @@ def __init__(
:type success_url: str
:param error_url: the error url
:type error_url: str
+ :param privacy_policy_url: the privacy policy url
+ :type privacy_policy_url: str
+ :param allow_handoff: boolean flag for allow_handoff
+ :type allow_handoff: bool
"""
self.__allowed_capture_methods = allowed_capture_methods
self.__primary_colour = primary_colour
@@ -48,6 +54,8 @@ def __init__(
self.__preset_issuing_country = preset_issuing_country
self.__success_url = success_url
self.__error_url = error_url
+ self.__privacy_policy_url = privacy_policy_url
+ self.__allow_handoff = allow_handoff
@property
def allowed_capture_methods(self):
@@ -121,6 +129,25 @@ def error_url(self):
"""
return self.__error_url
+ @property
+ def privacy_policy_url(self):
+ """
+ The privacy policy URL
+
+ :return: the privacy policy url
+ """
+ return self.__privacy_policy_url
+
+ @property
+ def allow_handoff(self):
+ """
+ Flag to enable/disable relying business to handoff
+ support when creating a session.
+
+ :return: the allow_handoff
+ """
+ return self.__allow_handoff
+
def to_json(self):
return remove_null_values(
{
@@ -132,6 +159,8 @@ def to_json(self):
"preset_issuing_country": self.preset_issuing_country,
"success_url": self.success_url,
"error_url": self.error_url,
+ "privacy_policy_url": self.privacy_policy_url,
+ "allow_handoff": self.allow_handoff,
}
)
@@ -150,6 +179,8 @@ def __init__(self):
self.__preset_issuing_country = None
self.__success_url = None
self.__error_url = None
+ self.__privacy_policy_url = None
+ self.__allow_handoff = None
def with_allowed_capture_methods(self, allowed_capture_methods):
"""
@@ -265,6 +296,30 @@ def with_error_url(self, url):
self.__error_url = url
return self
+ def with_privacy_policy_url(self, url):
+ """
+ Sets the privacy policy URL
+
+ :param url: the privacy policy URL
+ :type url: str
+ :return: the builder
+ :rtype: SdkConfigBuilder
+ """
+ self.__privacy_policy_url = url
+ return self
+
+ def with_allow_handoff(self, flag):
+ """
+ Sets the allow handoff flag
+
+ :param flag: boolean value for flag
+ :type flag: bool
+ :return: the builder
+ :rtype: SdkConfigBuilder
+ """
+ self.__allow_handoff = flag
+ return self
+
def build(self):
return SdkConfig(
self.__allowed_capture_methods,
@@ -275,4 +330,6 @@ def build(self):
self.__preset_issuing_country,
self.__success_url,
self.__error_url,
+ self.__allow_handoff,
+ self.__privacy_policy_url,
)
diff --git a/yoti_python_sdk/doc_scan/session/create/session_spec.py b/yoti_python_sdk/doc_scan/session/create/session_spec.py
index ef3bbce4..75a2b8be 100644
--- a/yoti_python_sdk/doc_scan/session/create/session_spec.py
+++ b/yoti_python_sdk/doc_scan/session/create/session_spec.py
@@ -21,6 +21,7 @@ def __init__(
requested_tasks=None,
required_documents=None,
block_biometric_consent=None,
+ session_deadline=None,
):
"""
:param client_session_token_ttl: the client session token TTL
@@ -41,6 +42,8 @@ def __init__(
:type required_documents: list[RequiredDocument] or None
:param block_biometric_consent: block the collection of biometric consent
:type block_biometric_consent: bool
+ :param session_deadline: session deadline using a Zoned timestamp
+ "type session_deadline: str
"""
if requested_tasks is None:
requested_tasks = []
@@ -58,6 +61,7 @@ def __init__(
self.__requested_tasks = requested_tasks
self.__required_documents = required_documents
self.__block_biometric_consent = block_biometric_consent
+ self.__session_deadline = session_deadline
@property
def client_session_token_ttl(self):
@@ -152,6 +156,16 @@ def block_biometric_consent(self):
"""
return self.__block_biometric_consent
+ @property
+ def session_deadline(self):
+ """
+ Session deadline used by IDV
+
+ :return: session deadline
+ :rtype: str
+ """
+ return self.__session_deadline
+
def to_json(self):
return remove_null_values(
{
@@ -164,6 +178,7 @@ def to_json(self):
"sdk_config": self.sdk_config,
"required_documents": self.required_documents,
"block_biometric_consent": self.block_biometric_consent,
+ "session_deadline": self.session_deadline,
}
)
@@ -183,6 +198,7 @@ def __init__(self):
self.__requested_tasks = []
self.__required_documents = []
self.__block_biometric_consent = None
+ self.__session_deadline = None
def with_client_session_token_ttl(self, value):
"""
@@ -196,6 +212,19 @@ def with_client_session_token_ttl(self, value):
self.__client_session_token_ttl = value
return self
+ def with_session_deadline(self, value):
+ """
+ Sets the deadline that the session needs to be completed by.
+ Can be used as an alternative to with_client_session_token_ttl.
+
+ :param value: the session deadline
+ :type value: str
+ :return: the builder
+ :rtype: SessionSpecBuilder
+ """
+ self.__session_deadline = value
+ return self
+
def with_resources_ttl(self, value):
"""
Sets the resources TTL (time-to-live)
@@ -309,4 +338,5 @@ def build(self):
self.__requested_tasks,
self.__required_documents,
self.__block_biometric_consent,
+ self.__session_deadline,
)
diff --git a/yoti_python_sdk/doc_scan/session/create/subcheck/__init__.py b/yoti_python_sdk/doc_scan/session/create/subcheck/__init__.py
new file mode 100644
index 00000000..2e248d33
--- /dev/null
+++ b/yoti_python_sdk/doc_scan/session/create/subcheck/__init__.py
@@ -0,0 +1,5 @@
+from .issuing_authority_sub_check import IssuingAuthoritySubCheckBuilder
+
+__all__ = [
+ "IssuingAuthoritySubCheckBuilder",
+]
diff --git a/yoti_python_sdk/doc_scan/session/create/subcheck/issuing_authority_sub_check.py b/yoti_python_sdk/doc_scan/session/create/subcheck/issuing_authority_sub_check.py
new file mode 100644
index 00000000..f39b426f
--- /dev/null
+++ b/yoti_python_sdk/doc_scan/session/create/subcheck/issuing_authority_sub_check.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+
+from yoti_python_sdk.doc_scan.session.create.filter.document_filter import (
+ DocumentFilter,
+)
+from yoti_python_sdk.utils import YotiSerializable
+from .sub_check import SubRequestedCheck
+
+
+class IssuingAuthoritySubCheck(SubRequestedCheck):
+ """
+ Requests creation of an Issuing Authority Sub Check.
+ """
+
+ def __init__(self, filter=None):
+ self._filter = filter
+
+ @property
+ def requested(self):
+ return True
+
+ @property
+ def filter(self):
+ return self._filter
+
+
+class IssuingAuthoritySubCheckBuilder:
+ """
+ Builder for Issuing Authority Sub Check.
+ """
+
+ def __init__(self):
+ self._filter = None
+
+ def with_filter(self, filter):
+ if not issubclass(type(filter), DocumentFilter):
+ raise ValueError("invalid filter")
+
+ self._filter = filter
+
+ return self
+
+ def build(self):
+ return IssuingAuthoritySubCheck(filter=self._filter)
diff --git a/yoti_python_sdk/doc_scan/session/create/subcheck/sub_check.py b/yoti_python_sdk/doc_scan/session/create/subcheck/sub_check.py
new file mode 100644
index 00000000..d4fa882d
--- /dev/null
+++ b/yoti_python_sdk/doc_scan/session/create/subcheck/sub_check.py
@@ -0,0 +1,20 @@
+from abc import ABCMeta
+from abc import abstractmethod
+
+from yoti_python_sdk.utils import YotiSerializable
+
+
+class SubRequestedCheck(YotiSerializable):
+ """
+ Requests creation of a SubCheck to be performed on a document
+ """
+
+ __metaclass__ = ABCMeta
+
+ @property
+ @abstractmethod
+ def type(self):
+ raise NotImplementedError
+
+ def to_json(self):
+ return remove_null_values({"type": self.type, "config": self.config})
diff --git a/yoti_python_sdk/doc_scan/session/retrieve/check_response.py b/yoti_python_sdk/doc_scan/session/retrieve/check_response.py
index f1cb515d..f05c4cf5 100644
--- a/yoti_python_sdk/doc_scan/session/retrieve/check_response.py
+++ b/yoti_python_sdk/doc_scan/session/retrieve/check_response.py
@@ -184,3 +184,11 @@ class SupplementaryDocumentTextDataCheckResponse(CheckResponse):
"""
pass
+
+
+class WatchlistScreeningCheckResponse(CheckResponse):
+ """
+ Represents a watchlist screening check for a given session
+ """
+
+ pass
diff --git a/yoti_python_sdk/doc_scan/session/retrieve/create_session_result.py b/yoti_python_sdk/doc_scan/session/retrieve/create_session_result.py
index c64dd9da..2990f473 100644
--- a/yoti_python_sdk/doc_scan/session/retrieve/create_session_result.py
+++ b/yoti_python_sdk/doc_scan/session/retrieve/create_session_result.py
@@ -16,6 +16,7 @@ def __init__(self, data=None):
data = dict()
self.__client_session_token_ttl = data.get("client_session_token_ttl", None)
+ self.__session_deadline = data.get("session_deadline", None)
self.__session_id = data.get("session_id", None)
self.__client_session_token = data.get("client_session_token", None)
@@ -49,3 +50,13 @@ def session_id(self):
:rtype: str or None
"""
return self.__session_id
+
+ @property
+ def session_deadline(self):
+ """
+ The deadline that the session needs to be completed by.
+
+ :return: the session deadline
+ :rtype: str or None
+ """
+ return self.__session_deadline
diff --git a/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py b/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py
index f39eed98..1d995d04 100644
--- a/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py
+++ b/yoti_python_sdk/doc_scan/session/retrieve/get_session_result.py
@@ -16,6 +16,7 @@
LivenessCheckResponse,
TextDataCheckResponse,
SupplementaryDocumentTextDataCheckResponse,
+ WatchlistScreeningCheckResponse,
)
from .resource_container import ResourceContainer
@@ -80,6 +81,7 @@ def __parse_check(check):
constants.ID_DOCUMENT_FACE_MATCH: FaceMatchCheckResponse,
constants.ID_DOCUMENT_TEXT_DATA_CHECK: TextDataCheckResponse,
constants.LIVENESS: LivenessCheckResponse,
+ constants.WATCHLIST_SCREENING_CHECK_TYPE: WatchlistScreeningCheckResponse,
constants.ID_DOCUMENT_COMPARISON: IDDocumentComparisonCheckResponse,
constants.SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK: SupplementaryDocumentTextDataCheckResponse,
}
diff --git a/yoti_python_sdk/doc_scan/support/supported_documents.py b/yoti_python_sdk/doc_scan/support/supported_documents.py
index 55505b78..a35a21e1 100644
--- a/yoti_python_sdk/doc_scan/support/supported_documents.py
+++ b/yoti_python_sdk/doc_scan/support/supported_documents.py
@@ -4,11 +4,16 @@ def __init__(self, data=None):
data = dict()
self.__type = data.get("type", None)
+ self.__is_strictly_latin = data.get("is_strictly_latin", None)
@property
def type(self):
return self.__type
+ @property
+ def is_strictly_latin(self):
+ return self.__is_strictly_latin
+
class SupportedCountry(object):
def __init__(self, data=None):
diff --git a/yoti_python_sdk/tests/doc_scan/session/create/check/test_wathclist_check.py b/yoti_python_sdk/tests/doc_scan/session/create/check/test_wathclist_check.py
new file mode 100644
index 00000000..3bfec82b
--- /dev/null
+++ b/yoti_python_sdk/tests/doc_scan/session/create/check/test_wathclist_check.py
@@ -0,0 +1,104 @@
+import json
+import unittest
+
+from yoti_python_sdk.doc_scan import constants
+from yoti_python_sdk.doc_scan.session.create.check import (
+ WatchlistScreeningCheckBuilder,
+)
+from yoti_python_sdk.doc_scan.session.create.check.requested_check import RequestedCheck
+from yoti_python_sdk.doc_scan.session.create.check.watchlist_screen import (
+ WatchlistScreeningCheck,
+ WatchlistScreeningCheckConfig,
+)
+from yoti_python_sdk.utils import YotiEncoder
+from yoti_python_sdk.doc_scan.constants import WATCHLIST_SCREENING_CHECK_TYPE
+
+
+class WatchlistScreeningCheckTest(unittest.TestCase):
+ def test_should_build_correctly_with_manual_check(self):
+ dummy_manual_check = "DUMMY_VALUE"
+
+ result = (
+ WatchlistScreeningCheckBuilder()
+ .with_manual_check(dummy_manual_check)
+ .build()
+ )
+
+ assert isinstance(result, RequestedCheck)
+ assert isinstance(result, WatchlistScreeningCheck)
+
+ assert result.type == constants.WATCHLIST_SCREENING_CHECK_TYPE
+ assert result.config.manual_check == dummy_manual_check
+ assert result.config.categories == []
+
+ def test_should_build_corretly_with_categories(self):
+ dummy_categories = ["FIRST", "SECOND"]
+
+ result = (
+ WatchlistScreeningCheckBuilder().with_categories(dummy_categories).build()
+ )
+
+ assert isinstance(result, RequestedCheck)
+ assert isinstance(result, WatchlistScreeningCheck)
+
+ assert result.type == constants.WATCHLIST_SCREENING_CHECK_TYPE
+ assert result.config.categories == dummy_categories
+ assert result.config.manual_check is None
+
+ def test_should_build_correctly_with_manual_check_and_categories(self):
+ dummy_manual_check = "DUMMY_VALUE"
+ dummy_categories = ["FIRST", "SECOND"]
+
+ result = (
+ WatchlistScreeningCheckBuilder()
+ .with_manual_check(dummy_manual_check)
+ .with_categories(dummy_categories)
+ .build()
+ )
+
+ assert isinstance(result, RequestedCheck)
+ assert isinstance(result, WatchlistScreeningCheck)
+
+ assert result.type == constants.WATCHLIST_SCREENING_CHECK_TYPE
+ assert result.config.manual_check == dummy_manual_check
+ assert result.config.categories == dummy_categories
+
+ def test_should_serialize_to_json_without_error(self):
+ another_dummy_manual_check = "DUMMY_VALUE"
+ another_dummy_categories = ["FIRST", "SECOND"]
+
+ result = (
+ WatchlistScreeningCheckBuilder()
+ .with_manual_check(another_dummy_manual_check)
+ .with_categories(another_dummy_categories)
+ .build()
+ )
+
+ s = json.dumps(result, cls=YotiEncoder)
+ assert s is not None and s != ""
+
+ result = (
+ WatchlistScreeningCheckBuilder()
+ .with_categories(another_dummy_categories)
+ .build()
+ )
+
+ s = json.dumps(result, cls=YotiEncoder)
+ assert s is not None and s != ""
+
+ s = json.loads(s)
+ assert s.get("type") == WATCHLIST_SCREENING_CHECK_TYPE
+ assert s.get("config") == {"categories": another_dummy_categories}
+
+ result = (
+ WatchlistScreeningCheckBuilder()
+ .with_manual_check(another_dummy_manual_check)
+ .build()
+ )
+
+ s = json.dumps(result, cls=YotiEncoder)
+ assert s is not None and s != ""
+
+ s = json.loads(s)
+ assert s.get("type") == WATCHLIST_SCREENING_CHECK_TYPE
+ assert s.get("config") == {"manual_check": "DUMMY_VALUE", "categories": []}
diff --git a/yoti_python_sdk/tests/doc_scan/session/create/subcheck/__init__.py b/yoti_python_sdk/tests/doc_scan/session/create/subcheck/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/yoti_python_sdk/tests/doc_scan/session/create/subcheck/test_issuing_authority_sub_check.py b/yoti_python_sdk/tests/doc_scan/session/create/subcheck/test_issuing_authority_sub_check.py
new file mode 100644
index 00000000..677dfc40
--- /dev/null
+++ b/yoti_python_sdk/tests/doc_scan/session/create/subcheck/test_issuing_authority_sub_check.py
@@ -0,0 +1,67 @@
+import unittest
+
+from yoti_python_sdk.doc_scan.session.create.filter.orthogonal_restrictions_filter import (
+ OrthogonalRestrictionsFilterBuilder,
+)
+from yoti_python_sdk.doc_scan.session.create.subcheck import (
+ IssuingAuthoritySubCheckBuilder,
+)
+from yoti_python_sdk.doc_scan.session.create.subcheck.issuing_authority_sub_check import (
+ IssuingAuthoritySubCheck,
+)
+
+
+class IssuingAuthoritySubCheckTest(unittest.TestCase):
+ def test_should_build_correctly_without_additional_data(self):
+ issuing_authority_sub_check = IssuingAuthoritySubCheckBuilder().build()
+
+ assert isinstance(issuing_authority_sub_check, IssuingAuthoritySubCheck)
+
+ def test_should_build_correctly_with_filter(self):
+ filter = (
+ OrthogonalRestrictionsFilterBuilder()
+ .with_whitelisted_country_codes(["GBR", "FRA"])
+ .with_whitelisted_document_types(["PASSPORT", "STATE_ID"])
+ .build()
+ )
+
+ issuing_authority_sub_check = (
+ IssuingAuthoritySubCheckBuilder().with_filter(filter=filter).build()
+ )
+
+ assert isinstance(issuing_authority_sub_check, IssuingAuthoritySubCheck)
+ assert issuing_authority_sub_check.filter == filter
+
+ def test_should_always_build_with_requested_as_boolean_true(self):
+ issuing_authority_sub_check = IssuingAuthoritySubCheckBuilder().build()
+
+ assert issuing_authority_sub_check.requested is True
+
+ def test_allow_non_latin_documents_set_to_true(self):
+ filter = (
+ OrthogonalRestrictionsFilterBuilder().allow_non_latin_documents().build()
+ )
+
+ assert filter.allow_non_latin_documents is True
+
+ def test_allow_non_latin_documents_set_to_false(self):
+ filter = (
+ OrthogonalRestrictionsFilterBuilder().disable_non_latin_documents().build()
+ )
+
+ assert filter.allow_non_latin_documents is False
+
+ def test_default_non_latin_documents(self):
+ filter = OrthogonalRestrictionsFilterBuilder().build()
+
+ assert "allow_non_latin_documents" not in filter.to_json()
+
+ def test_build_invalid_filter(self):
+ filter = "invalid"
+
+ with self.assertRaises(ValueError):
+ IssuingAuthoritySubCheckBuilder().with_filter(filter=filter).build()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/yoti_python_sdk/tests/doc_scan/session/create/test_notification_config.py b/yoti_python_sdk/tests/doc_scan/session/create/test_notification_config.py
index f207ff14..1fa09b29 100644
--- a/yoti_python_sdk/tests/doc_scan/session/create/test_notification_config.py
+++ b/yoti_python_sdk/tests/doc_scan/session/create/test_notification_config.py
@@ -12,6 +12,7 @@ class NotificationConfigTest(unittest.TestCase):
SOME_AUTH_TOKEN = "someAuthToken"
SOME_ENDPOINT = "someEndpoint"
SOME_TOPIC = "someTopic"
+ SOME_AUTH_TYPE = "someAuthType"
def test_should_build_correctly(self):
result = (
@@ -26,6 +27,7 @@ def test_should_build_correctly(self):
assert result.auth_token is self.SOME_AUTH_TOKEN
assert result.endpoint is self.SOME_ENDPOINT
assert self.SOME_TOPIC in result.topics
+ assert result.auth_type is None
def test_should_add_resource_update_topic(self):
result = (
@@ -104,6 +106,30 @@ def test_should_store_unique_topics(self):
assert len(result.topics) == 1
+ def test_build_with_basic_auth_type(self):
+ result = (
+ NotificationConfigBuilder()
+ .with_auth_token(self.SOME_AUTH_TOKEN)
+ .with_endpoint(self.SOME_ENDPOINT)
+ .with_topic(self.SOME_TOPIC)
+ .with_basic_auth_type()
+ .build()
+ )
+
+ assert result.auth_type is "BASIC"
+
+ def test_build_with_bearer_auth_type(self):
+ result = (
+ NotificationConfigBuilder()
+ .with_auth_token(self.SOME_AUTH_TOKEN)
+ .with_endpoint(self.SOME_ENDPOINT)
+ .with_topic(self.SOME_TOPIC)
+ .with_bearer_auth_type()
+ .build()
+ )
+
+ assert result.auth_type is "BEARER"
+
def test_should_serialize_to_json_without_error(self):
result = (
NotificationConfigBuilder()
diff --git a/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py b/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py
index 6de7b4ca..d621a441 100644
--- a/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py
+++ b/yoti_python_sdk/tests/doc_scan/session/create/test_sdk_config.py
@@ -14,6 +14,8 @@ class SdkConfigTest(unittest.TestCase):
SOME_PRESET_ISSUING_COUNTRY = "USA"
SOME_SUCCESS_URL = "https://mysite.com/yoti/success"
SOME_ERROR_URL = "https://mysite.com/yoti/error"
+ SOME_PRIVACY_POLICY_URL = "https://mysite.com/privacy"
+ SOME_ALLOW_HANDOFF = True
def test_should_build_correctly(self):
result = (
@@ -26,6 +28,8 @@ def test_should_build_correctly(self):
.with_preset_issuing_country(self.SOME_PRESET_ISSUING_COUNTRY)
.with_success_url(self.SOME_SUCCESS_URL)
.with_error_url(self.SOME_ERROR_URL)
+ .with_privacy_policy_url(self.SOME_PRIVACY_POLICY_URL)
+ .with_allow_handoff(self.SOME_ALLOW_HANDOFF)
.build()
)
@@ -38,12 +42,24 @@ def test_should_build_correctly(self):
assert result.preset_issuing_country is self.SOME_PRESET_ISSUING_COUNTRY
assert result.success_url is self.SOME_SUCCESS_URL
assert result.error_url is self.SOME_ERROR_URL
+ assert result.privacy_policy_url is self.SOME_PRIVACY_POLICY_URL
+ assert result.allow_handoff is True
def test_should_allows_camera(self):
result = SdkConfigBuilder().with_allows_camera().build()
assert result.allowed_capture_methods == "CAMERA"
+ def test_not_passing_allow_handoff(self):
+ result = SdkConfigBuilder().with_allows_camera().build()
+
+ assert result.allow_handoff is None
+
+ def test_passing_allow_handoff_false_value(self):
+ result = SdkConfigBuilder().with_allow_handoff(False).build()
+
+ assert result.allow_handoff is False
+
def test_should_serialize_to_json_without_error(self):
result = (
SdkConfigBuilder()
@@ -55,6 +71,7 @@ def test_should_serialize_to_json_without_error(self):
.with_preset_issuing_country(self.SOME_PRESET_ISSUING_COUNTRY)
.with_success_url(self.SOME_SUCCESS_URL)
.with_error_url(self.SOME_ERROR_URL)
+ .with_privacy_policy_url(self.SOME_PRIVACY_POLICY_URL)
.build()
)
diff --git a/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py b/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py
index 41f0a5e5..034c4b05 100644
--- a/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py
+++ b/yoti_python_sdk/tests/doc_scan/session/create/test_session_spec.py
@@ -21,6 +21,7 @@ class SessionSpecTest(unittest.TestCase):
SOME_CLIENT_SESSION_TOKEN_TTL = 300
SOME_RESOURCES_TTL = 100000
SOME_USER_TRACKING_ID = "someUserTrackingId"
+ SOME_SESSION_DEADLINE = "2021-09-03T11:40:54.727619+02:00"
def test_should_build_correctly(self):
sdk_config_mock = Mock(spec=SdkConfig)
@@ -31,6 +32,7 @@ def test_should_build_correctly(self):
result = (
SessionSpecBuilder()
.with_client_session_token_ttl(self.SOME_CLIENT_SESSION_TOKEN_TTL)
+ .with_session_deadline(self.SOME_SESSION_DEADLINE)
.with_resources_ttl(self.SOME_RESOURCES_TTL)
.with_user_tracking_id(self.SOME_USER_TRACKING_ID)
.with_notifications(notification_mock)
@@ -49,6 +51,7 @@ def test_should_build_correctly(self):
assert requested_check_mock in result.requested_checks
assert len(result.requested_tasks) == 1
assert requested_task_mock in result.requested_tasks
+ assert result.session_deadline == self.SOME_SESSION_DEADLINE
def test_should_serialize_to_json_without_error(self):
sdk_config_mock = Mock(spec=SdkConfig)
@@ -66,6 +69,7 @@ def test_should_serialize_to_json_without_error(self):
result = (
SessionSpecBuilder()
.with_client_session_token_ttl(self.SOME_CLIENT_SESSION_TOKEN_TTL)
+ .with_session_deadline(self.SOME_SESSION_DEADLINE)
.with_resources_ttl(self.SOME_RESOURCES_TTL)
.with_user_tracking_id(self.SOME_USER_TRACKING_ID)
.with_notifications(notification_mock)
diff --git a/yoti_python_sdk/tests/doc_scan/support/test_supported_documents.py b/yoti_python_sdk/tests/doc_scan/support/test_supported_documents.py
index 762d509d..a2887318 100644
--- a/yoti_python_sdk/tests/doc_scan/support/test_supported_documents.py
+++ b/yoti_python_sdk/tests/doc_scan/support/test_supported_documents.py
@@ -18,6 +18,24 @@ def test_supported_document_should_not_throw_exception_on_missing_data():
assert result.type is None
+def test_supported_document_created_with_is_strictly_latin_as_true():
+ result = SupportedDocument({"is_strictly_latin": True})
+
+ assert result.is_strictly_latin is True
+
+
+def test_supported_document_created_with_is_strictly_latin_as_false():
+ result = SupportedDocument({"is_strictly_latin": False})
+
+ assert result.is_strictly_latin is False
+
+
+def test_supported_document_created_without_is_strictly_latin():
+ result = SupportedDocument({"type": "someSupportedDocument"})
+
+ assert result.is_strictly_latin is None
+
+
def test_supported_country_should_parse_data():
data = {
"code": "someCode",
diff --git a/yoti_python_sdk/tests/dynamic_sharing_service/extension/test_third_party_attribute_extension.py b/yoti_python_sdk/tests/dynamic_sharing_service/extension/test_third_party_attribute_extension.py
index 7c7a57ae..1bf054ba 100644
--- a/yoti_python_sdk/tests/dynamic_sharing_service/extension/test_third_party_attribute_extension.py
+++ b/yoti_python_sdk/tests/dynamic_sharing_service/extension/test_third_party_attribute_extension.py
@@ -117,9 +117,18 @@ def test_should_format_utc_expiry_dates_correctly(expiry_date, expected_value):
@pytest.mark.parametrize(
"expiry_date, tz_name",
[
- (datetime(2030, 6, 6, 8, 0, 0, 0), "US/Eastern",),
- (datetime(2030, 6, 6, 15, 0, 0, 0), "Europe/Moscow",),
- (datetime(2030, 6, 6, 7, 0, 0, 0), "America/Jamaica",),
+ (
+ datetime(2030, 6, 6, 8, 0, 0, 0),
+ "US/Eastern",
+ ),
+ (
+ datetime(2030, 6, 6, 15, 0, 0, 0),
+ "Europe/Moscow",
+ ),
+ (
+ datetime(2030, 6, 6, 7, 0, 0, 0),
+ "America/Jamaica",
+ ),
(datetime(2030, 6, 6, 23, 0, 0, 0), "Etc/GMT-11"),
(datetime(2030, 6, 6, 7, 0, 0, 0), "Etc/GMT+5"),
# In order to conform with the POSIX style, those zones beginning
diff --git a/yoti_python_sdk/tests/test_activity_details.py b/yoti_python_sdk/tests/test_activity_details.py
index 1735b030..1fc349f1 100644
--- a/yoti_python_sdk/tests/test_activity_details.py
+++ b/yoti_python_sdk/tests/test_activity_details.py
@@ -85,7 +85,7 @@ def create_age_verified_field(
):
activity_details.field = lambda: None
activity_details.field.name = (
- "age_over:{0}".format(age) if over is True else "age_under:".format(age)
+ "age_over:{0}".format(age) if over is True else "age_under:{0}".format(age)
)
activity_details.field.value = encoded_string_verified_value
activity_details.field.content_type = Protobuf.CT_STRING
diff --git a/yoti_python_sdk/tests/test_anchor.py b/yoti_python_sdk/tests/test_anchor.py
index 0adf48aa..7305d6fa 100644
--- a/yoti_python_sdk/tests/test_anchor.py
+++ b/yoti_python_sdk/tests/test_anchor.py
@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
import logging
+import pytz
import time
from datetime import datetime
-
-from yoti_python_sdk.protobuf.attribute_public_api import Attribute_pb2
+from datetime import timedelta
import yoti_python_sdk
from yoti_python_sdk import config
from yoti_python_sdk.anchor import Anchor
+from yoti_python_sdk.protobuf.attribute_public_api import Attribute_pb2
from yoti_python_sdk.tests import anchor_fixture_parser
@@ -118,11 +119,10 @@ def test_processing_unknown_anchor_data():
(anchor.value, anchor.anchor_type, anchor.sub_type) for anchor in anchors
]
- expected_timestamp = datetime(2019, 3, 5, 10, 45, 11, 840037)
- actual_timestamp = anchors[0].signed_timestamp
+ expected_timestamp = datetime(2019, 3, 5, 10, 45, 11, 840037, tzinfo=pytz.utc)
+ actual_timestamp = anchors[0].signed_timestamp.astimezone(pytz.utc)
assert expected_timestamp == actual_timestamp
-
assert "document-registration-server" in [
a.value for a in anchors[0].origin_server_certs.issuer
]
diff --git a/yoti_python_sdk/version.py b/yoti_python_sdk/version.py
index 18b49b72..f697b811 100644
--- a/yoti_python_sdk/version.py
+++ b/yoti_python_sdk/version.py
@@ -1,2 +1,2 @@
# -*- coding: utf-8 -*-
-__version__ = "2.14.0"
+__version__ = "2.15.0"