vcon 0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vcon-0.1/LICENSE +21 -0
- vcon-0.1/PKG-INFO +111 -0
- vcon-0.1/README.md +73 -0
- vcon-0.1/pyproject.toml +37 -0
- vcon-0.1/setup.cfg +4 -0
- vcon-0.1/tests/test_dates.py +59 -0
- vcon-0.1/tests/test_external_content.py +231 -0
- vcon-0.1/tests/test_filter_plugins.py +95 -0
- vcon-0.1/tests/test_jwe.py +244 -0
- vcon-0.1/tests/test_migrate.py +25 -0
- vcon-0.1/tests/test_minimalist.py +266 -0
- vcon-0.1/tests/test_party_search.py +43 -0
- vcon-0.1/tests/test_redis_mgr.py +39 -0
- vcon-0.1/tests/test_signature.py +325 -0
- vcon-0.1/tests/test_uuid.py +57 -0
- vcon-0.1/tests/test_vcon_cli.py +183 -0
- vcon-0.1/tests/test_whisper_plugin.py +135 -0
- vcon-0.1/vcon/__init__.py +1353 -0
- vcon-0.1/vcon/cli.py +337 -0
- vcon-0.1/vcon/filter_plugins/__init__.py +268 -0
- vcon-0.1/vcon/filter_plugins/impl/whisper.py +145 -0
- vcon-0.1/vcon/filter_plugins/whisper.py +9 -0
- vcon-0.1/vcon/security.py +373 -0
- vcon-0.1/vcon/utils.py +81 -0
- vcon-0.1/vcon.egg-info/PKG-INFO +111 -0
- vcon-0.1/vcon.egg-info/SOURCES.txt +27 -0
- vcon-0.1/vcon.egg-info/dependency_links.txt +1 -0
- vcon-0.1/vcon.egg-info/requires.txt +10 -0
- vcon-0.1/vcon.egg-info/top_level.txt +1 -0
vcon-0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 vcon-dev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
vcon-0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: vcon
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: vCons are PDFs for human conversations, defining them so they can be shared, analyzed and secured.
|
|
5
|
+
Author-email: Daniel Petrie <dan.ietf@sipez.com>, Thomas McCarthy-Howe <thomas.howe@strolid.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2022 vcon-dev
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: repository, https://github.com/vcon-dev/vcon
|
|
29
|
+
Project-URL: documentation, https://www.vcon.dev/
|
|
30
|
+
Keywords: vcon,conversational ai,conversation
|
|
31
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
32
|
+
Classifier: Programming Language :: Python
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Requires-Python: >=3.8
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
License-File: LICENSE
|
|
38
|
+
|
|
39
|
+
# The Home Repo for vCons and the Conserver
|
|
40
|
+
|
|
41
|
+
## Introduction
|
|
42
|
+
vCons are PDFs for human conversations, defining them so they can be shared, analyzed and secured. The Conserver is a domain specific data platform based on vCons, converting the raw materials of recorded conversations into self-serve data sources for any team. The Conserver represents the most modern set of tools for data engineers to responsibly and scalably use customer conversations in data pipelines.
|
|
43
|
+
|
|
44
|
+
The Vcon library consists of two primary components:
|
|
45
|
+
|
|
46
|
+
* The Python Vcon package for constructing and operating on Vcon objects
|
|
47
|
+
* The Conserver for storing, managing and manipulating Vcon objects and operation streams on Vcon objects
|
|
48
|
+
|
|
49
|
+
## Table of Contents
|
|
50
|
+
|
|
51
|
+
+ [Presentations, Whitepapers and Tutorials](#presentations-whitepapers-and-tutorials)
|
|
52
|
+
+ [vCon Library Quick Start for Python](https://github.com/vcon-dev/vcon/wiki/Library-Quick-Start)
|
|
53
|
+
+ [Testing the Vcon Package](#testing-the-vcon-package)
|
|
54
|
+
+ [Testing the conserver](#testing-the-conserver)
|
|
55
|
+
|
|
56
|
+
## Presentations, Whitepapers and Tutorials
|
|
57
|
+
|
|
58
|
+
See the [Birds of a Feather session at IETF 116, Yokohama](https://youtu.be/EF2OMbo6Qj4)
|
|
59
|
+
|
|
60
|
+
See the [presentation at TADSummit](https://youtu.be/ZBRJ6FcVblc)
|
|
61
|
+
|
|
62
|
+
See the [presentation at IETF](https://youtu.be/dJsPzZITr_g?t=243)
|
|
63
|
+
|
|
64
|
+
See the [presentation at IIT](https://youtu.be/s-pjgpBOQqc)
|
|
65
|
+
|
|
66
|
+
Read the [IETF draft proposal](https://datatracker.ietf.org/doc/html/draft-petrie-vcon-01)
|
|
67
|
+
|
|
68
|
+
Read the [white paper](https://docs.google.com/document/d/1TV8j29knVoOJcZvMHVFDaan0OVfraH_-nrS5gW4-DEA/edit?usp=sharing)
|
|
69
|
+
|
|
70
|
+
See the [key note proposal for vCons](https://blog.tadsummit.com/2021/12/08/strolid-keynote-vcons/).
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
## Testing the Vcon Package
|
|
74
|
+
A suite of pytest unit tests exist for the Vcon package in: [tests](tests)
|
|
75
|
+
|
|
76
|
+
These can be run using the following command in the current directory:
|
|
77
|
+
|
|
78
|
+
pytest -v -rP tests
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
Please also run separately the following unit test as it will check for spurious stdout from the Vcon package that will likely cause the CLI to break:
|
|
82
|
+
|
|
83
|
+
pytest -v -rP tests/test_vcon_cli.py
|
|
84
|
+
|
|
85
|
+
Note: These errors may not show up when you run test_vcon_cli.py with the rest of the unit tests as some stdout may only occur when the Vcon package is first imported and may not get trapped/detected by other unit tests.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## Testing the conserver
|
|
89
|
+
A suite of pytest unit tests exist for the conserver in: [server/tests](server/tests)
|
|
90
|
+
|
|
91
|
+
Running and testing the conserver requires a running instance of Redis.
|
|
92
|
+
Be sure to create and edit your server/.env file to reflect your redis server address and port.
|
|
93
|
+
It can be generated like the following command line:
|
|
94
|
+
|
|
95
|
+
cat <<EOF>.env
|
|
96
|
+
#!/usr/bin/sh
|
|
97
|
+
export AWS_BUCKET=vcon-storage
|
|
98
|
+
export AWS_KEY_ID=aaaaaaaaaaaaaaa
|
|
99
|
+
export AWS_SECRET_KEY=bbbbbbbbbbb
|
|
100
|
+
export DEEPGRAM_KEY=ccccccccccccc
|
|
101
|
+
export ENV=dev
|
|
102
|
+
export HOSTNAME=http://0.0.0.0:8000
|
|
103
|
+
export REDIS_URL=redis://172.17.0.4:6379
|
|
104
|
+
#export MONOREPO_DATABASE_URL=postgresql://password:userid@your.postgres.domain.com:5432/postgres
|
|
105
|
+
EOF
|
|
106
|
+
|
|
107
|
+
The unit tests for the conserver can be run using the following command in the server directory:
|
|
108
|
+
|
|
109
|
+
source .env
|
|
110
|
+
pytest -v -rP tests
|
|
111
|
+
|
vcon-0.1/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# The Home Repo for vCons and the Conserver
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
vCons are PDFs for human conversations, defining them so they can be shared, analyzed and secured. The Conserver is a domain specific data platform based on vCons, converting the raw materials of recorded conversations into self-serve data sources for any team. The Conserver represents the most modern set of tools for data engineers to responsibly and scalably use customer conversations in data pipelines.
|
|
5
|
+
|
|
6
|
+
The Vcon library consists of two primary components:
|
|
7
|
+
|
|
8
|
+
* The Python Vcon package for constructing and operating on Vcon objects
|
|
9
|
+
* The Conserver for storing, managing and manipulating Vcon objects and operation streams on Vcon objects
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
+ [Presentations, Whitepapers and Tutorials](#presentations-whitepapers-and-tutorials)
|
|
14
|
+
+ [vCon Library Quick Start for Python](https://github.com/vcon-dev/vcon/wiki/Library-Quick-Start)
|
|
15
|
+
+ [Testing the Vcon Package](#testing-the-vcon-package)
|
|
16
|
+
+ [Testing the conserver](#testing-the-conserver)
|
|
17
|
+
|
|
18
|
+
## Presentations, Whitepapers and Tutorials
|
|
19
|
+
|
|
20
|
+
See the [Birds of a Feather session at IETF 116, Yokohama](https://youtu.be/EF2OMbo6Qj4)
|
|
21
|
+
|
|
22
|
+
See the [presentation at TADSummit](https://youtu.be/ZBRJ6FcVblc)
|
|
23
|
+
|
|
24
|
+
See the [presentation at IETF](https://youtu.be/dJsPzZITr_g?t=243)
|
|
25
|
+
|
|
26
|
+
See the [presentation at IIT](https://youtu.be/s-pjgpBOQqc)
|
|
27
|
+
|
|
28
|
+
Read the [IETF draft proposal](https://datatracker.ietf.org/doc/html/draft-petrie-vcon-01)
|
|
29
|
+
|
|
30
|
+
Read the [white paper](https://docs.google.com/document/d/1TV8j29knVoOJcZvMHVFDaan0OVfraH_-nrS5gW4-DEA/edit?usp=sharing)
|
|
31
|
+
|
|
32
|
+
See the [key note proposal for vCons](https://blog.tadsummit.com/2021/12/08/strolid-keynote-vcons/).
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Testing the Vcon Package
|
|
36
|
+
A suite of pytest unit tests exist for the Vcon package in: [tests](tests)
|
|
37
|
+
|
|
38
|
+
These can be run using the following command in the current directory:
|
|
39
|
+
|
|
40
|
+
pytest -v -rP tests
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Please also run separately the following unit test as it will check for spurious stdout from the Vcon package that will likely cause the CLI to break:
|
|
44
|
+
|
|
45
|
+
pytest -v -rP tests/test_vcon_cli.py
|
|
46
|
+
|
|
47
|
+
Note: These errors may not show up when you run test_vcon_cli.py with the rest of the unit tests as some stdout may only occur when the Vcon package is first imported and may not get trapped/detected by other unit tests.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Testing the conserver
|
|
51
|
+
A suite of pytest unit tests exist for the conserver in: [server/tests](server/tests)
|
|
52
|
+
|
|
53
|
+
Running and testing the conserver requires a running instance of Redis.
|
|
54
|
+
Be sure to create and edit your server/.env file to reflect your redis server address and port.
|
|
55
|
+
It can be generated like the following command line:
|
|
56
|
+
|
|
57
|
+
cat <<EOF>.env
|
|
58
|
+
#!/usr/bin/sh
|
|
59
|
+
export AWS_BUCKET=vcon-storage
|
|
60
|
+
export AWS_KEY_ID=aaaaaaaaaaaaaaa
|
|
61
|
+
export AWS_SECRET_KEY=bbbbbbbbbbb
|
|
62
|
+
export DEEPGRAM_KEY=ccccccccccccc
|
|
63
|
+
export ENV=dev
|
|
64
|
+
export HOSTNAME=http://0.0.0.0:8000
|
|
65
|
+
export REDIS_URL=redis://172.17.0.4:6379
|
|
66
|
+
#export MONOREPO_DATABASE_URL=postgresql://password:userid@your.postgres.domain.com:5432/postgres
|
|
67
|
+
EOF
|
|
68
|
+
|
|
69
|
+
The unit tests for the conserver can be run using the following command in the server directory:
|
|
70
|
+
|
|
71
|
+
source .env
|
|
72
|
+
pytest -v -rP tests
|
|
73
|
+
|
vcon-0.1/pyproject.toml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vcon"
|
|
7
|
+
version = "0.1"
|
|
8
|
+
description = "vCons are PDFs for human conversations, defining them so they can be shared, analyzed and secured."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [{ name = "Daniel Petrie", email = "dan.ietf@sipez.com" }, { name = "Thomas McCarthy-Howe", email = "thomas.howe@strolid.com" }]
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
classifiers = [
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
]
|
|
17
|
+
keywords = ["vcon", "conversational ai", "conversation"]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"cryptography >= 37",
|
|
20
|
+
"hsslms >= 0.1.2",
|
|
21
|
+
"python-jose[cryptography] >= 3.3.0",
|
|
22
|
+
"python-dateutil >= 2.8.2",
|
|
23
|
+
"uuid6 >= 2023.4.25",
|
|
24
|
+
"python-json-logger >= 2.0.7",
|
|
25
|
+
"sox >= 1.4.1",
|
|
26
|
+
]
|
|
27
|
+
requires-python = ">=3.8"
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = ["pytest"]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
repository = "https://github.com/vcon-dev/vcon"
|
|
34
|
+
documentation = "https://www.vcon.dev/"
|
|
35
|
+
|
|
36
|
+
[tool.setuptools]
|
|
37
|
+
packages = ["vcon", "vcon.filter_plugins","vcon.filter_plugins.impl"]
|
vcon-0.1/setup.cfg
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit test for date conversions
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import vcon.utils
|
|
6
|
+
import datetime
|
|
7
|
+
import dateutil.tz
|
|
8
|
+
|
|
9
|
+
date_int = 1652552179
|
|
10
|
+
date_float = 1652552179.0001
|
|
11
|
+
date_rfc2822 = "Wed, 14 May 2022 18:16:19 -0000"
|
|
12
|
+
date_rfc3339 = "2022-05-14T18:16:19.000+00:00"
|
|
13
|
+
date_rfc3339_EDT = "2022-05-14T14:16:19.000-04:00"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_created_at():
|
|
17
|
+
v = vcon.Vcon()
|
|
18
|
+
print("created_at: {}".format(v.created_at))
|
|
19
|
+
assert(len(v.created_at) > 10)
|
|
20
|
+
|
|
21
|
+
def test_cannonize_date():
|
|
22
|
+
cannonized = vcon.utils.cannonize_date(date_int)
|
|
23
|
+
#print(cannonized)
|
|
24
|
+
assert(cannonized == date_rfc3339)
|
|
25
|
+
|
|
26
|
+
cannonized = vcon.utils.cannonize_date(date_float)
|
|
27
|
+
# off by time zone
|
|
28
|
+
#print(cannonized)
|
|
29
|
+
assert(cannonized == date_rfc3339)
|
|
30
|
+
|
|
31
|
+
cannonized = vcon.utils.cannonize_date(date_rfc2822)
|
|
32
|
+
# off by time zone
|
|
33
|
+
#print(cannonized)
|
|
34
|
+
assert(cannonized == date_rfc3339)
|
|
35
|
+
|
|
36
|
+
cannonized = vcon.utils.cannonize_date(date_rfc3339)
|
|
37
|
+
# off by time zone
|
|
38
|
+
#print(cannonized)
|
|
39
|
+
assert(cannonized == date_rfc3339)
|
|
40
|
+
|
|
41
|
+
cannonized = vcon.utils.cannonize_date(date_rfc3339_EDT)
|
|
42
|
+
# off by time zone
|
|
43
|
+
#print(cannonized)
|
|
44
|
+
assert(cannonized == date_rfc3339)
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
cannonized = vcon.utils.cannonize_date("10 June 2022 8:55PM")
|
|
48
|
+
raise Exception("Should have raised AttributeError exception for not valid date format")
|
|
49
|
+
|
|
50
|
+
except AttributeError as e:
|
|
51
|
+
# Expect to catch exception here
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
datetime_val = datetime.datetime(2022, 9, 27, 14, 23, 38, 938223)
|
|
55
|
+
datetime_val = datetime_val.replace(tzinfo = dateutil.tz.gettz('US/Eastern'))
|
|
56
|
+
cannonized = vcon.utils.cannonize_date(datetime_val)
|
|
57
|
+
#print("{} dst: {}".format(cannonized, datetime_val.dst()))
|
|
58
|
+
assert(cannonized == "2022-09-27T18:23:38.938+00:00")
|
|
59
|
+
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for external content such as recording, attachments which are stored
|
|
3
|
+
as URLs with a signature for the content stored else where. Using
|
|
4
|
+
Leighton-Micali One Time Signature (RFC8554).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import datetime
|
|
9
|
+
import pytest
|
|
10
|
+
import vcon
|
|
11
|
+
import vcon.security
|
|
12
|
+
import hsslms
|
|
13
|
+
import hashlib
|
|
14
|
+
import jose.utils
|
|
15
|
+
|
|
16
|
+
call_data = {
|
|
17
|
+
"epoch" : "1652552179",
|
|
18
|
+
"destination" : "2117",
|
|
19
|
+
"source" : "+19144345359",
|
|
20
|
+
"rfc2822" : "Sat, 14 May 2022 18:16:19 -0000",
|
|
21
|
+
"rfc3339" : "2022-05-14T18:16:19.000+00:00",
|
|
22
|
+
"file_extension" : "WAV",
|
|
23
|
+
"duration" : 94.84,
|
|
24
|
+
"channels" : 1
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@pytest.fixture(scope="function")
|
|
28
|
+
def empty_vcon() -> vcon.Vcon:
|
|
29
|
+
""" construct vCon with no data """
|
|
30
|
+
vCon = vcon.Vcon()
|
|
31
|
+
return(vCon)
|
|
32
|
+
|
|
33
|
+
@pytest.fixture(scope="function")
|
|
34
|
+
def two_party_tel_vcon(empty_vcon : vcon.Vcon) -> vcon.Vcon:
|
|
35
|
+
""" construct vCon with two tel URL """
|
|
36
|
+
vCon = empty_vcon
|
|
37
|
+
first_party = vCon.set_party_parameter("tel", call_data['source'])
|
|
38
|
+
second_party = vCon.set_party_parameter("tel", call_data['destination'])
|
|
39
|
+
vCon.set_uuid("vcon.dev")
|
|
40
|
+
return(vCon)
|
|
41
|
+
|
|
42
|
+
def test_lm_ots_sign() -> None:
|
|
43
|
+
file_size = 2048
|
|
44
|
+
fake_file = os.urandom(file_size)
|
|
45
|
+
|
|
46
|
+
key, sig = vcon.security.lm_one_time_signature(fake_file)
|
|
47
|
+
|
|
48
|
+
vcon.security.verify_lm_one_time_signature(fake_file, sig, key)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
# cause signature to fail
|
|
52
|
+
vcon.security.verify_lm_one_time_signature(fake_file, sig.replace("D", "E", 1), key)
|
|
53
|
+
raise Exception("Should have raisee and INVALID signature error")
|
|
54
|
+
|
|
55
|
+
except hsslms.utils.INVALID as invalid_error:
|
|
56
|
+
# Expect this to be raised as we have modified signature
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
# cause key to fail
|
|
61
|
+
vcon.security.verify_lm_one_time_signature(fake_file, sig, key.replace("A", "Z", 1))
|
|
62
|
+
raise Exception("Should have raised and INVALID key error")
|
|
63
|
+
|
|
64
|
+
except hsslms.utils.INVALID as invalid_error:
|
|
65
|
+
# Expect this to be raised as we have modified signature
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def test_get_external_recording(two_party_tel_vcon : vcon.Vcon) -> None:
|
|
69
|
+
# Add external ref
|
|
70
|
+
file_path = "examples/agent_sample.wav"
|
|
71
|
+
url = "https://github.com/vcon-dev/vcon/blob/main/examples/agent_sample.wav?raw=true"
|
|
72
|
+
file_content = b""
|
|
73
|
+
with open(file_path, "rb") as file_handle:
|
|
74
|
+
file_content = file_handle.read()
|
|
75
|
+
print("body length: {}".format(len(file_content)))
|
|
76
|
+
assert(len(file_content) > 10000)
|
|
77
|
+
|
|
78
|
+
dialog_index = two_party_tel_vcon.add_dialog_external_recording(file_content,
|
|
79
|
+
datetime.datetime.utcnow(),
|
|
80
|
+
0, # duration TODO
|
|
81
|
+
[0,1],
|
|
82
|
+
url,
|
|
83
|
+
vcon.Vcon.MIMETYPE_AUDIO_WAV,
|
|
84
|
+
os.path.basename(file_path))
|
|
85
|
+
|
|
86
|
+
assert(dialog_index == 0)
|
|
87
|
+
|
|
88
|
+
dialog_object = two_party_tel_vcon.dialog[0]
|
|
89
|
+
assert(dialog_object.get("originator", None) is None)
|
|
90
|
+
assert(dialog_object.get("duration", None) == 0)
|
|
91
|
+
assert(dialog_object.get("url", None) == url)
|
|
92
|
+
|
|
93
|
+
body_bytes = two_party_tel_vcon.get_dialog_external_recording(dialog_index)
|
|
94
|
+
assert(len(file_content) == len(body_bytes))
|
|
95
|
+
assert(file_content == body_bytes)
|
|
96
|
+
|
|
97
|
+
# This should be valid
|
|
98
|
+
two_party_tel_vcon.verify_dialog_external_recording(0, body_bytes)
|
|
99
|
+
|
|
100
|
+
body_byte_array = bytearray(body_bytes)
|
|
101
|
+
body_byte_array[0] = body_bytes[0] + 1
|
|
102
|
+
body_bytes = bytes(body_byte_array)
|
|
103
|
+
assert(file_content != body_bytes)
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
#We modified the body_bytes so this should fail
|
|
107
|
+
two_party_tel_vcon.verify_dialog_external_recording(0, body_bytes)
|
|
108
|
+
raise Exception("Should have thrown invalid as body_bytes is not identical")
|
|
109
|
+
|
|
110
|
+
except vcon.InvalidVconHash as invalid_body_error:
|
|
111
|
+
# We expect this to occur
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
def test_external_recording_lm_ots(two_party_tel_vcon : vcon.Vcon) -> None:
|
|
115
|
+
data_size = 4096
|
|
116
|
+
data = os.urandom(data_size)
|
|
117
|
+
|
|
118
|
+
url = "https://example.com?q=\"ddd\"&y=\'!\'"
|
|
119
|
+
#print("url: {}".format(url))
|
|
120
|
+
|
|
121
|
+
file_name = "my_rec.wav"
|
|
122
|
+
|
|
123
|
+
assert(vcon.Vcon.MIMETYPE_AUDIO_WAV == "audio/x-wav")
|
|
124
|
+
|
|
125
|
+
two_party_tel_vcon.add_dialog_external_recording(data,
|
|
126
|
+
call_data["rfc2822"],
|
|
127
|
+
call_data["duration"],
|
|
128
|
+
0,
|
|
129
|
+
url,
|
|
130
|
+
vcon.Vcon.MIMETYPE_AUDIO_WAV,
|
|
131
|
+
file_name,
|
|
132
|
+
sign_type="LM-OTS")
|
|
133
|
+
|
|
134
|
+
vcon_json = two_party_tel_vcon.dumps()
|
|
135
|
+
|
|
136
|
+
new_vcon = vcon.Vcon()
|
|
137
|
+
new_vcon.loads(vcon_json)
|
|
138
|
+
|
|
139
|
+
assert(len(new_vcon.dialog) == 1)
|
|
140
|
+
assert(new_vcon.dialog[0]['type'] == "recording")
|
|
141
|
+
assert(new_vcon.dialog[0]['url'] == url)
|
|
142
|
+
assert(new_vcon.dialog[0]['parties'] == 0)
|
|
143
|
+
assert(new_vcon.dialog[0]['start'] == call_data["rfc3339"])
|
|
144
|
+
assert(new_vcon.dialog[0]['duration'] == call_data["duration"])
|
|
145
|
+
assert(new_vcon.dialog[0]['mimetype'] == "audio/x-wav")
|
|
146
|
+
assert(new_vcon.dialog[0]['filename'] == file_name)
|
|
147
|
+
assert(new_vcon.dialog[0]['alg'] == 'LMOTS_SHA256_N32_W8')
|
|
148
|
+
assert(len(new_vcon.dialog[0]['key']) > 1)
|
|
149
|
+
assert("body" not in new_vcon.dialog[0])
|
|
150
|
+
|
|
151
|
+
new_vcon.verify_dialog_external_recording(0, data)
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
# Change the data so that validation should fail
|
|
155
|
+
new_vcon.verify_dialog_external_recording(0, data[1:])
|
|
156
|
+
raise Exception("Should have raised exception here as data is missin the first byte")
|
|
157
|
+
|
|
158
|
+
except hsslms.utils.INVALID as invalid_error:
|
|
159
|
+
# Expect to get this exception
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
def test_sha512():
|
|
163
|
+
data1 = b"test some text stuff as binary"
|
|
164
|
+
data_size = 4096
|
|
165
|
+
data2 = os.urandom(data_size)
|
|
166
|
+
|
|
167
|
+
hasher = hashlib.sha512()
|
|
168
|
+
|
|
169
|
+
hasher.update(data1)
|
|
170
|
+
hasher.update(data2)
|
|
171
|
+
|
|
172
|
+
sig_hash = jose.utils.base64url_encode(hasher.digest())
|
|
173
|
+
|
|
174
|
+
validater = hashlib.sha512()
|
|
175
|
+
|
|
176
|
+
validater.update(data1 + data2)
|
|
177
|
+
|
|
178
|
+
validate_hash = jose.utils.base64url_encode(validater.digest())
|
|
179
|
+
|
|
180
|
+
assert(sig_hash == validate_hash)
|
|
181
|
+
|
|
182
|
+
def test_external_recording_sha_512(two_party_tel_vcon : vcon.Vcon) -> None:
|
|
183
|
+
data_size = 4096
|
|
184
|
+
data = os.urandom(data_size)
|
|
185
|
+
|
|
186
|
+
url = "https://example.com?q=\"ddd\"&y=\'!\'"
|
|
187
|
+
#print("url: {}".format(url))
|
|
188
|
+
|
|
189
|
+
file_name = "my_rec.wav"
|
|
190
|
+
|
|
191
|
+
assert(vcon.Vcon.MIMETYPE_AUDIO_WAV == "audio/x-wav")
|
|
192
|
+
|
|
193
|
+
two_party_tel_vcon.add_dialog_external_recording(data,
|
|
194
|
+
call_data["rfc2822"],
|
|
195
|
+
call_data["duration"],
|
|
196
|
+
0,
|
|
197
|
+
url,
|
|
198
|
+
vcon.Vcon.MIMETYPE_AUDIO_WAV,
|
|
199
|
+
file_name,
|
|
200
|
+
originator=1)
|
|
201
|
+
|
|
202
|
+
vcon_json = two_party_tel_vcon.dumps()
|
|
203
|
+
|
|
204
|
+
print("original: {}".format(vcon_json))
|
|
205
|
+
new_vcon = vcon.Vcon()
|
|
206
|
+
new_vcon.loads(vcon_json)
|
|
207
|
+
print("deserialized: {}".format(new_vcon.dumps()))
|
|
208
|
+
|
|
209
|
+
assert(len(new_vcon.dialog) == 1)
|
|
210
|
+
assert(new_vcon.dialog[0]['type'] == "recording")
|
|
211
|
+
assert(new_vcon.dialog[0]['url'] == url)
|
|
212
|
+
assert(new_vcon.dialog[0]['parties'] == 0)
|
|
213
|
+
assert(new_vcon.dialog[0]['start'] == call_data["rfc3339"])
|
|
214
|
+
assert(new_vcon.dialog[0]['duration'] == call_data["duration"])
|
|
215
|
+
assert(new_vcon.dialog[0]['mimetype'] == "audio/x-wav")
|
|
216
|
+
assert(new_vcon.dialog[0]['filename'] == file_name)
|
|
217
|
+
assert(new_vcon.dialog[0]['alg'] == 'SHA-512')
|
|
218
|
+
assert(new_vcon.dialog[0]['originator'] == 1)
|
|
219
|
+
assert("body" not in new_vcon.dialog[0])
|
|
220
|
+
assert("key" not in new_vcon.dialog[0])
|
|
221
|
+
|
|
222
|
+
new_vcon.verify_dialog_external_recording(0, data)
|
|
223
|
+
|
|
224
|
+
try:
|
|
225
|
+
# Change the data so that validation should fail
|
|
226
|
+
new_vcon.verify_dialog_external_recording(0, data[1:])
|
|
227
|
+
raise Exception("Should have raised exception here as data is missin the first byte")
|
|
228
|
+
|
|
229
|
+
except vcon.InvalidVconHash as invalid_error:
|
|
230
|
+
# Expect to get this exception
|
|
231
|
+
pass
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
""" Unit test for filter plugin framework """
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import sys
|
|
5
|
+
import vcon
|
|
6
|
+
import vcon.filter_plugins
|
|
7
|
+
|
|
8
|
+
# test foo registration file
|
|
9
|
+
import tests.foo_reg
|
|
10
|
+
|
|
11
|
+
def test_registry():
|
|
12
|
+
plugin_names = vcon.filter_plugins.FilterPluginRegistry.get_names()
|
|
13
|
+
|
|
14
|
+
print("found {} plugins: {}".format(len(plugin_names), plugin_names))
|
|
15
|
+
|
|
16
|
+
# Test foo a test plugin, not fully implemented
|
|
17
|
+
plugin_foop = vcon.filter_plugins.FilterPluginRegistry.get("foop")
|
|
18
|
+
assert(plugin_foop is not None)
|
|
19
|
+
assert(plugin_foop.import_plugin() == True)
|
|
20
|
+
try:
|
|
21
|
+
plugin_foop.filter(None)
|
|
22
|
+
# SHould not get here
|
|
23
|
+
raise Exception("Should have thrown a PluginFilterNotImplemented exception")
|
|
24
|
+
|
|
25
|
+
except vcon.filter_plugins.PluginFilterNotImplemented as not_found_error:
|
|
26
|
+
# We are expecting this exception
|
|
27
|
+
print("got {}".format(not_found_error), file=sys.stderr)
|
|
28
|
+
#raise not_found_error
|
|
29
|
+
|
|
30
|
+
# this time test foop using its registered name as a method
|
|
31
|
+
try:
|
|
32
|
+
in_vcon = vcon.Vcon()
|
|
33
|
+
out_vcon = in_vcon.foop()
|
|
34
|
+
# SHould not get here
|
|
35
|
+
raise Exception("Should have thrown a PluginFilterNotImplemented exception")
|
|
36
|
+
|
|
37
|
+
except vcon.filter_plugins.PluginFilterNotImplemented as not_found_error:
|
|
38
|
+
# We are expecting this exception
|
|
39
|
+
print("got {}".format(not_found_error), file=sys.stderr)
|
|
40
|
+
#raise not_found_error
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
plugin_barp = vcon.filter_plugins.FilterPluginRegistry.get("barp")
|
|
44
|
+
raise Exception("Expected not to fine barp and throw exception")
|
|
45
|
+
|
|
46
|
+
except vcon.filter_plugins.PluginFilterNotRegistered as not_reg_error:
|
|
47
|
+
print(not_reg_error, file=sys.stderr)
|
|
48
|
+
|
|
49
|
+
vcon.filter_plugins.FilterPluginRegistry.set_type_default_name("exclaim", "foop")
|
|
50
|
+
assert(vcon.filter_plugins.FilterPluginRegistry.get_type_default_name("exclaim") == "foop")
|
|
51
|
+
assert(vcon.filter_plugins.FilterPluginRegistry.get_type_default_name("bar") == None)
|
|
52
|
+
assert(vcon.filter_plugins.FilterPluginRegistry.get_type_default_plugin("exclaim") == plugin_foop)
|
|
53
|
+
|
|
54
|
+
# this time test foop using it set as default type exclaim name as a method
|
|
55
|
+
try:
|
|
56
|
+
in_vcon = vcon.Vcon()
|
|
57
|
+
out_vcon = in_vcon.exclaim()
|
|
58
|
+
# SHould not get here
|
|
59
|
+
raise Exception("Should have thrown a PluginFilterNotImplemented exception")
|
|
60
|
+
|
|
61
|
+
except vcon.filter_plugins.PluginFilterNotImplemented as not_found_error:
|
|
62
|
+
# We are expecting this exception
|
|
63
|
+
print("got {}".format(not_found_error), file=sys.stderr)
|
|
64
|
+
#raise not_found_error
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# Test that real plugin was registered
|
|
68
|
+
plugin_whisper = vcon.filter_plugins.FilterPluginRegistry.get("whisper")
|
|
69
|
+
assert(plugin_whisper is not None)
|
|
70
|
+
assert(plugin_whisper.import_plugin() == True)
|
|
71
|
+
|
|
72
|
+
# Verify whisper is the default transcribe type filter plugin
|
|
73
|
+
assert(vcon.filter_plugins.FilterPluginRegistry.get_type_default_name("transcribe") == "whisper")
|
|
74
|
+
|
|
75
|
+
in_vcon = vcon.Vcon()
|
|
76
|
+
options = {}
|
|
77
|
+
try:
|
|
78
|
+
out_vcon = in_vcon.filter("doesnotexist", **options)
|
|
79
|
+
raise Exception("Expected not to find plugin and throw exception")
|
|
80
|
+
|
|
81
|
+
except vcon.filter_plugins.PluginFilterNotRegistered as not_reg_error:
|
|
82
|
+
print(not_reg_error, file=sys.stderr)
|
|
83
|
+
|
|
84
|
+
import tests.bar_reg
|
|
85
|
+
|
|
86
|
+
v2 = vcon.Vcon()
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
v2.barp()
|
|
90
|
+
raise Exception("expect exception as filter plugin bar trys to import a non-existant package")
|
|
91
|
+
|
|
92
|
+
except vcon.filter_plugins.PluginModuleNotFound as fp_no_mod_error:
|
|
93
|
+
# should get here
|
|
94
|
+
print("got {}".format(fp_no_mod_error))
|
|
95
|
+
|