diff --git a/.builder/actions/crt-ci-test.py b/.builder/actions/crt-ci-test.py new file mode 100644 index 00000000..af57d98b --- /dev/null +++ b/.builder/actions/crt-ci-test.py @@ -0,0 +1,29 @@ +import Builder +import re + +class CiTest(Builder.Action): + + def _write_environment_script_secret_to_env(self, env, secret_name): + mqtt5_ci_environment_script = env.shell.get_secret(secret_name) + env_line = re.compile('^export\s+(\w+)=(.+)') + + lines = mqtt5_ci_environment_script.splitlines() + for line in lines: + env_pair_match = env_line.match(line) + if env_pair_match.group(1) and env_pair_match.group(2): + env.shell.setenv(env_pair_match.group(1), env_pair_match.group(2), quiet=True) + + + def run(self, env): + + actions = [] + + try: + self._write_environment_script_secret_to_env(env, "ci/sdk-unit-testing") + + env.shell.exec(["python3", "-m", "unittest", "discover", "--verbose"], check=True) + except: + print(f'Failure while running tests') + actions.append("exit 1") + + return Builder.Script(actions, name='ci-test') diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c07625c..d1891cfe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,6 @@ env: CI_UTILS_FOLDER: "./aws-iot-device-sdk-python-v2/utils" CI_SAMPLES_CFG_FOLDER: "./aws-iot-device-sdk-python-v2/.github/workflows" CI_SAMPLES_FOLDER: "./aws-iot-device-sdk-python-v2/samples" - CI_IOT_CONTAINERS_ROLE: ${{ secrets.AWS_CI_IOT_CONTAINERS }} CI_PUBSUB_ROLE: ${{ secrets.AWS_CI_PUBSUB_ROLE }} CI_COGNITO_ROLE: ${{ secrets.AWS_CI_COGNITO_ROLE }} CI_X509_ROLE: ${{ secrets.AWS_CI_X509_ROLE }} @@ -31,77 +30,27 @@ env: CI_FLEET_PROVISIONING_ROLE: ${{ secrets.AWS_CI_FLEET_PROVISIONING_ROLE }} CI_DEVICE_ADVISOR: ${{ secrets.AWS_CI_DEVICE_ADVISOR_ROLE }} CI_MQTT5_ROLE: ${{ secrets.AWS_CI_MQTT5_ROLE }} + CI_BUILD_AND_TEST_ROLE: arn:aws:iam::180635532705:role/V2_SDK_Unit_Testing jobs: - all-python-versions: - runs-on: ubuntu-latest - permissions: - id-token: write # This is required for requesting the JWT - steps: - - name: Build ${{ env.PACKAGE_NAME }} - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_DA_ROLE_KEY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_DA_ROLE_PRIVATE_KEY }} - # There's hackery in builder.json so that when we run on manylinux - # we build and test using every version of python that we support. - run: | - aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh - ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-manylinux2014-x64 build -p ${{ env.PACKAGE_NAME }} - - al2: - runs-on: ubuntu-latest + windows: + runs-on: windows-latest permissions: id-token: write # This is required for requesting the JWT steps: - - name: configure AWS credentials (containers) - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: ${{ env.CI_IOT_CONTAINERS_ROLE }} - aws-region: ${{ env.AWS_DEFAULT_REGION }} - # We can't use the `uses: docker://image` version yet, GitHub lacks authentication for actions -> packages - - name: Build ${{ env.PACKAGE_NAME }} + - name: Install boto3 run: | - aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh - ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-al2-x64 build -p ${{ env.PACKAGE_NAME }} - - - raspberry: - runs-on: ubuntu-latest # latest - permissions: - id-token: write # This is required for requesting the JWT - strategy: - fail-fast: false - matrix: - image: - - raspbian-bullseye - steps: + python -m pip install boto3 - name: configure AWS credentials (containers) - uses: aws-actions/configure-aws-credentials@v1 + uses: aws-actions/configure-aws-credentials@v2 with: - role-to-assume: ${{ env.CI_IOT_CONTAINERS_ROLE }} + role-to-assume: ${{ env.CI_BUILD_AND_TEST_ROLE }} aws-region: ${{ env.AWS_DEFAULT_REGION }} - # set arm arch - - name: Install qemu/docker - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - - name: Build ${{ env.PACKAGE_NAME }} - run: | - aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh - ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ matrix.image }} build -p ${{ env.PACKAGE_NAME }} - - - windows: - runs-on: windows-latest - permissions: - id-token: write # This is required for requesting the JWT - steps: - name: Build ${{ env.PACKAGE_NAME }} run: | python -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder.pyz')" python builder.pyz build -p ${{ env.PACKAGE_NAME }} - - name: Running samples in CI setup - run: | - python -m pip install boto3 - name: configure AWS credentials (PubSub) uses: aws-actions/configure-aws-credentials@v1 with: @@ -136,6 +85,14 @@ jobs: permissions: id-token: write # This is required for requesting the JWT steps: + - name: Install boto3 + run: | + python3 -m pip install boto3 + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_BUILD_AND_TEST_ROLE }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} run: | python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" @@ -175,6 +132,14 @@ jobs: permissions: id-token: write # This is required for requesting the JWT steps: + - name: Running samples in CI setup + run: | + python -m pip install boto3 + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_BUILD_AND_TEST_ROLE }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} - name: Build ${{ env.PACKAGE_NAME }} run: | python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" @@ -215,17 +180,22 @@ jobs: permissions: id-token: write # This is required for requesting the JWT steps: - - name: Build ${{ env.PACKAGE_NAME }} - run: | - python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" - chmod a+x builder - ./builder build -p ${{ env.PACKAGE_NAME }} - name: Running samples in CI setup run: | python3 -m pip install boto3 sudo apt-get update -y sudo apt-get install softhsm -y softhsm2-util --version + - name: configure AWS credentials (containers) + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.CI_BUILD_AND_TEST_ROLE }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} + - name: Build ${{ env.PACKAGE_NAME }} + run: | + python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')" + chmod a+x builder + ./builder build -p ${{ env.PACKAGE_NAME }} - name: configure AWS credentials (Connect and PubSub) uses: aws-actions/configure-aws-credentials@v1 with: diff --git a/awsiot/mqtt5_client_builder.py b/awsiot/mqtt5_client_builder.py index 2824599b..23aa7098 100644 --- a/awsiot/mqtt5_client_builder.py +++ b/awsiot/mqtt5_client_builder.py @@ -569,6 +569,8 @@ def direct_with_custom_authorizer( auth_authorizer_name=None, auth_authorizer_signature=None, auth_password=None, + auth_token_key_name=None, + auth_token_value=None, **kwargs) -> awscrt.mqtt5.Client: """ This builder creates an :class:`awscrt.mqtt5.Client`, configured for an MQTT5 Client using a custom @@ -581,17 +583,30 @@ def direct_with_custom_authorizer( auth_username (`str`): The username to use with the custom authorizer. If provided, the username given will be passed when connecting to the custom authorizer. If not provided, it will check to see if a username has already been set (via username="example") - and will use that instead. - If no username has been set then no username will be sent with the MQTT connection. - - auth_authorizer_name (`str`): The name of the custom authorizer. - If not provided, then "x-amz-customauthorizer-name" will not be added with the MQTT connection. - - auth_authorizer_signature (`str`): The signature of the custom authorizer. - If not provided, then "x-amz-customauthorizer-name" will not be added with the MQTT connection. + and will use that instead. Custom authentication parameters will be appended as appropriate + to any supplied username value. auth_password (`str`): The password to use with the custom authorizer. - If not provided, then no passord will be set. + If not provided, then no password will be sent in the initial CONNECT packet. + + auth_authorizer_name (`str`): Name of the custom authorizer to use. + Required if the endpoint does not have a default custom authorizer associated with it. It is strongly + suggested to URL-encode this value; the SDK will not do so for you. + + auth_authorizer_signature (`str`): The digital signature of the token value in the `auth_token_value` + parameter. The signature must be based on the private key associated with the custom authorizer. The + signature must be base64 encoded. + Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode this value; + the SDK will not do so for you. + + auth_token_key_name (`str`): Key used to extract the custom authorizer token from MQTT username query-string + properties. + Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode + this value; the SDK will not do so for you. + + auth_token_value (`str`): An opaque token value. This value must be signed by the private key associated with + the custom authorizer and the result passed in via the `auth_authorizer_signature` parameter. + Required if the custom authorizer has signing enabled. """ _check_required_kwargs(**kwargs) @@ -606,10 +621,14 @@ def direct_with_custom_authorizer( if auth_authorizer_name is not None: username_string = _add_to_username_parameter( username_string, auth_authorizer_name, "x-amz-customauthorizer-name=") + if auth_authorizer_signature is not None: username_string = _add_to_username_parameter( username_string, auth_authorizer_signature, "x-amz-customauthorizer-signature=") + if auth_token_key_name is not None and auth_token_value is not None: + username_string = _add_to_username_parameter(username_string, auth_token_value, auth_token_key_name + "=") + kwargs["username"] = username_string kwargs["password"] = auth_password @@ -628,6 +647,8 @@ def websockets_with_custom_authorizer( auth_authorizer_signature=None, auth_password=None, websocket_proxy_options=None, + auth_token_key_name=None, + auth_token_value=None, **kwargs) -> awscrt.mqtt5.Client: """ This builder creates an :class:`awscrt.mqtt5.Client`, configured for an MQTT5 Client using a custom @@ -640,17 +661,30 @@ def websockets_with_custom_authorizer( auth_username (`str`): The username to use with the custom authorizer. If provided, the username given will be passed when connecting to the custom authorizer. If not provided, it will check to see if a username has already been set (via username="example") - and will use that instead. - If no username has been set then no username will be sent with the MQTT connection. + and will use that instead. Custom authentication parameters will be appended as appropriate + to any supplied username value. + + auth_password (`str`): The password to use with the custom authorizer. + If not provided, then no password will be sent in the initial CONNECT packet. - auth_authorizer_name (`str`): The name of the custom authorizer. - If not provided, then "x-amz-customauthorizer-name" will not be added with the MQTT connection. + auth_authorizer_name (`str`): Name of the custom authorizer to use. + Required if the endpoint does not have a default custom authorizer associated with it. It is strongly + suggested to URL-encode this value; the SDK will not do so for you. - auth_authorizer_signature (`str`): The signature of the custom authorizer. - If not provided, then "x-amz-customauthorizer-name" will not be added with the MQTT connection. + auth_authorizer_signature (`str`): The digital signature of the token value in the `auth_token_value` + parameter. The signature must be based on the private key associated with the custom authorizer. The + signature must be base64 encoded. + Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode this value; + the SDK will not do so for you. - auth_password (`str`): The password to use with the custom authorizer. - If not provided, then no passord will be set. + auth_token_key_name (`str`): Key used to extract the custom authorizer token from MQTT username query-string + properties. + Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode + this value; the SDK will not do so for you. + + auth_token_value (`str`): An opaque token value. This value must be signed by the private key associated with + the custom authorizer and the result passed in via the `auth_authorizer_signature` parameter. + Required if the custom authorizer has signing enabled. websocket_proxy_options (awscrt.http.HttpProxyOptions): Deprecated, for proxy settings use `http_proxy_options` (described in @@ -669,10 +703,14 @@ def websockets_with_custom_authorizer( if auth_authorizer_name is not None: username_string = _add_to_username_parameter( username_string, auth_authorizer_name, "x-amz-customauthorizer-name=") + if auth_authorizer_signature is not None: username_string = _add_to_username_parameter( username_string, auth_authorizer_signature, "x-amz-customauthorizer-signature=") + if auth_token_key_name is not None and auth_token_value is not None: + username_string = _add_to_username_parameter(username_string, auth_token_value, auth_token_key_name + "=") + kwargs["username"] = username_string kwargs["password"] = auth_password diff --git a/awsiot/mqtt_connection_builder.py b/awsiot/mqtt_connection_builder.py index e6f69c29..50c95aa9 100644 --- a/awsiot/mqtt_connection_builder.py +++ b/awsiot/mqtt_connection_builder.py @@ -453,6 +453,8 @@ def direct_with_custom_authorizer( auth_authorizer_name=None, auth_authorizer_signature=None, auth_password=None, + auth_token_key_name=None, + auth_token_value=None, **kwargs) -> awscrt.mqtt.Connection: """ This builder creates an :class:`awscrt.mqtt.Connection`, configured for an MQTT connection using a custom @@ -465,17 +467,30 @@ def direct_with_custom_authorizer( auth_username (`str`): The username to use with the custom authorizer. If provided, the username given will be passed when connecting to the custom authorizer. If not provided, it will check to see if a username has already been set (via username="example") - and will use that instead. - If no username has been set then no username will be sent with the MQTT connection. - - auth_authorizer_name (`str`): The name of the custom authorizer. - If not provided, then "x-amz-customauthorizer-name" will not be added with the MQTT connection. - - auth_authorizer_signature (`str`): The signature of the custom authorizer. - If not provided, then "x-amz-customauthorizer-name" will not be added with the MQTT connection. + and will use that instead. Custom authentication parameters will be appended as appropriate + to any supplied username value. auth_password (`str`): The password to use with the custom authorizer. - If not provided, then no passord will be set. + If not provided, then no password will be sent in the initial CONNECT packet. + + auth_authorizer_name (`str`): Name of the custom authorizer to use. + Required if the endpoint does not have a default custom authorizer associated with it. It is strongly + suggested to URL-encode this value; the SDK will not do so for you. + + auth_authorizer_signature (`str`): The digital signature of the token value in the `auth_token_value` + parameter. The signature must be based on the private key associated with the custom authorizer. The + signature must be base64 encoded. + Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode this value; + the SDK will not do so for you. + + auth_token_key_name (`str`): Key used to extract the custom authorizer token from MQTT username query-string + properties. + Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode + this value; the SDK will not do so for you. + + auth_token_value (`str`): An opaque token value. This value must be signed by the private key associated with + the custom authorizer and the result passed in via the `auth_authorizer_signature` parameter. + Required if the custom authorizer has signing enabled. """ return _with_custom_authorizer( @@ -483,16 +498,20 @@ def direct_with_custom_authorizer( auth_authorizer_name=auth_authorizer_name, auth_authorizer_signature=auth_authorizer_signature, auth_password=auth_password, + auth_token_key_name=auth_token_key_name, + auth_token_value=auth_token_value, use_websockets=False, **kwargs) def websockets_with_custom_authorizer( - region, - credentials_provider, + region=None, + credentials_provider=None, auth_username=None, auth_authorizer_name=None, auth_authorizer_signature=None, auth_password=None, + auth_token_key_name=None, + auth_token_value=None, **kwargs) -> awscrt.mqtt.Connection: """ This builder creates an :class:`awscrt.mqtt.Connection`, configured for an MQTT connection using a custom @@ -509,17 +528,30 @@ def websockets_with_custom_authorizer( auth_username (`str`): The username to use with the custom authorizer. If provided, the username given will be passed when connecting to the custom authorizer. If not provided, it will check to see if a username has already been set (via username="example") - and will use that instead. - If no username has been set then no username will be sent with the MQTT connection. - - auth_authorizer_name (`str`): The name of the custom authorizer. - If not provided, then "x-amz-customauthorizer-name" will not be added with the MQTT connection. - - auth_authorizer_signature (`str`): The signature of the custom authorizer. - If not provided, then "x-amz-customauthorizer-name" will not be added with the MQTT connection. + and will use that instead. Custom authentication parameters will be appended as appropriate + to any supplied username value. auth_password (`str`): The password to use with the custom authorizer. - If not provided, then no passord will be set. + If not provided, then no password will be sent in the initial CONNECT packet. + + auth_authorizer_name (`str`): Name of the custom authorizer to use. + Required if the endpoint does not have a default custom authorizer associated with it. It is strongly + suggested to URL-encode this value; the SDK will not do so for you. + + auth_authorizer_signature (`str`): The digital signature of the token value in the `auth_token_value` + parameter. The signature must be based on the private key associated with the custom authorizer. The + signature must be base64 encoded. + Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode this value; + the SDK will not do so for you. + + auth_token_key_name (`str`): Key used to extract the custom authorizer token from MQTT username query-string + properties. + Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode + this value; the SDK will not do so for you. + + auth_token_value (`str`): An opaque token value. This value must be signed by the private key associated with + the custom authorizer and the result passed in via the `auth_authorizer_signature` parameter. + Required if the custom authorizer has signing enabled. """ return _with_custom_authorizer( @@ -527,6 +559,8 @@ def websockets_with_custom_authorizer( auth_authorizer_name=auth_authorizer_name, auth_authorizer_signature=auth_authorizer_signature, auth_password=auth_password, + auth_token_key_name=auth_token_key_name, + auth_token_value=auth_token_value, use_websockets=True, websockets_region=region, websockets_credentials_provider=credentials_provider, @@ -537,6 +571,8 @@ def _with_custom_authorizer(auth_username=None, auth_authorizer_name=None, auth_authorizer_signature=None, auth_password=None, + auth_token_key_name=None, + auth_token_value=None, use_websockets=False, websockets_credentials_provider=None, websockets_region=None, @@ -557,10 +593,14 @@ def _with_custom_authorizer(auth_username=None, if auth_authorizer_name is not None: username_string = _add_to_username_parameter( username_string, auth_authorizer_name, "x-amz-customauthorizer-name=") + if auth_authorizer_signature is not None: username_string = _add_to_username_parameter( username_string, auth_authorizer_signature, "x-amz-customauthorizer-signature=") + if auth_token_key_name is not None and auth_token_value is not None: + username_string = _add_to_username_parameter(username_string, auth_token_value, auth_token_key_name + "=") + kwargs["username"] = username_string kwargs["password"] = auth_password @@ -572,17 +612,7 @@ def _with_custom_authorizer(auth_username=None, def _sign_websocket_handshake_request(transform_args, **kwargs): # transform_args need to know when transform is done try: - signing_config = awscrt.auth.AwsSigningConfig( - algorithm=awscrt.auth.AwsSigningAlgorithm.V4, - signature_type=awscrt.auth.AwsSignatureType.HTTP_REQUEST_QUERY_PARAMS, - credentials_provider=websockets_credentials_provider, - region=websockets_region, - service='iotdevicegateway', - omit_session_token=True, # IoT is weird and does not sign X-Amz-Security-Token - ) - - signing_future = awscrt.auth.aws_sign_request(transform_args.http_request, signing_config) - signing_future.add_done_callback(lambda x: transform_args.set_done(x.exception())) + transform_args.set_done() except Exception as e: transform_args.set_done(e) diff --git a/builder.json b/builder.json index 23e69139..d80fba66 100644 --- a/builder.json +++ b/builder.json @@ -84,16 +84,11 @@ "build": [ ["{python}", "-c", "print('ignore this fake build step')"] ], - "test": [ - ["{python}", "-m", "pip", "install", "--upgrade", "pip", "setuptools"], - ["{python}", "-m", "pip", "install", ".", "--verbose"], - ["{python}", "-m", "pip", "install", "boto3", "autopep8"], - ["{python}", "-m", "unittest", "discover", "--verbose"] - ], "build_steps": [ "python3 -m pip install ." ], "test_steps": [ + "ci-test" ], "env": { } diff --git a/test/test_mqtt.py b/test/test_mqtt.py index ff805437..abe7e877 100644 --- a/test/test_mqtt.py +++ b/test/test_mqtt.py @@ -14,6 +14,20 @@ PROXY_HOST = os.environ.get('proxyhost') PROXY_PORT = int(os.environ.get('proxyport', '0')) +CUSTOM_AUTHORIZER_ENDPOINT = os.environ.get("CUSTOM_AUTHORIZER_ENDPOINT") +CUSTOM_AUTHORIZER_NAME_SIGNED = os.environ.get("CUSTOM_AUTHORIZER_NAME_SIGNED") +CUSTOM_AUTHORIZER_NAME_UNSIGNED = os.environ.get("CUSTOM_AUTHORIZER_NAME_UNSIGNED") +CUSTOM_AUTHORIZER_PASSWORD = os.environ.get("CUSTOM_AUTHORIZER_PASSWORD") +CUSTOM_AUTHORIZER_SIGNATURE = os.environ.get("CUSTOM_AUTHORIZER_SIGNATURE") +CUSTOM_AUTHORIZER_TOKEN_KEY_NAME = os.environ.get("CUSTOM_AUTHORIZER_TOKEN_KEY_NAME") +CUSTOM_AUTHORIZER_TOKEN_VALUE = os.environ.get("CUSTOM_AUTHORIZER_TOKEN_VALUE") + + +def has_custom_auth_environment(): + return (CUSTOM_AUTHORIZER_ENDPOINT is not None) and (CUSTOM_AUTHORIZER_NAME_SIGNED is not None) and \ + (CUSTOM_AUTHORIZER_NAME_UNSIGNED is not None) and (CUSTOM_AUTHORIZER_PASSWORD is not None) and \ + (CUSTOM_AUTHORIZER_SIGNATURE is not None) and (CUSTOM_AUTHORIZER_TOKEN_KEY_NAME is not None) and \ + (CUSTOM_AUTHORIZER_TOKEN_VALUE is not None) class Config: cache = None @@ -159,3 +173,77 @@ def test_websockets_proxy(self): client_id=create_client_id(), client_bootstrap=bootstrap) self._test_connection(connection) + + @unittest.skipIf(not has_custom_auth_environment(), 'requires custom authentication env vars') + def test_mqtt311_builder_direct_signed_custom_authorizer(self): + elg = EventLoopGroup() + resolver = DefaultHostResolver(elg) + bootstrap = ClientBootstrap(elg, resolver) + + connection = mqtt_connection_builder.direct_with_custom_authorizer( + auth_username="", + auth_authorizer_name=CUSTOM_AUTHORIZER_NAME_SIGNED, + auth_authorizer_signature=CUSTOM_AUTHORIZER_SIGNATURE, + auth_password=CUSTOM_AUTHORIZER_PASSWORD, + auth_token_key_name=CUSTOM_AUTHORIZER_TOKEN_KEY_NAME, + auth_token_value=CUSTOM_AUTHORIZER_TOKEN_VALUE, + endpoint=CUSTOM_AUTHORIZER_ENDPOINT, + client_id=create_client_id(), + client_bootstrap=bootstrap) + + self._test_connection(connection) + + @unittest.skipIf(not has_custom_auth_environment(), 'requires custom authentication env vars') + def test_mqtt311_builder_direct_unsigned_custom_authorizer(self): + elg = EventLoopGroup() + resolver = DefaultHostResolver(elg) + bootstrap = ClientBootstrap(elg, resolver) + + connection = mqtt_connection_builder.direct_with_custom_authorizer( + auth_username="", + auth_authorizer_name=CUSTOM_AUTHORIZER_NAME_SIGNED, + auth_authorizer_signature=CUSTOM_AUTHORIZER_SIGNATURE, + auth_password=CUSTOM_AUTHORIZER_PASSWORD, + auth_token_key_name=CUSTOM_AUTHORIZER_TOKEN_KEY_NAME, + auth_token_value=CUSTOM_AUTHORIZER_TOKEN_VALUE, + endpoint=CUSTOM_AUTHORIZER_ENDPOINT, + client_id=create_client_id(), + client_bootstrap=bootstrap) + + self._test_connection(connection) + + @unittest.skipIf(not has_custom_auth_environment(), 'requires custom authentication env vars') + def test_mqtt311_builder_websocket_unsigned_custom_authorizer(self): + elg = EventLoopGroup() + resolver = DefaultHostResolver(elg) + bootstrap = ClientBootstrap(elg, resolver) + + connection = mqtt_connection_builder.websockets_with_custom_authorizer( + auth_username="", + auth_authorizer_name=CUSTOM_AUTHORIZER_NAME_UNSIGNED, + auth_password=CUSTOM_AUTHORIZER_PASSWORD, + endpoint=CUSTOM_AUTHORIZER_ENDPOINT, + client_id=create_client_id(), + client_bootstrap=bootstrap) + + self._test_connection(connection) + + @unittest.skipIf(not has_custom_auth_environment(), 'requires custom authentication env vars') + def test_mqtt311_builder_websocket_signed_custom_authorizer(self): + elg = EventLoopGroup() + resolver = DefaultHostResolver(elg) + bootstrap = ClientBootstrap(elg, resolver) + + connection = mqtt_connection_builder.websockets_with_custom_authorizer( + auth_username="", + auth_authorizer_name=CUSTOM_AUTHORIZER_NAME_SIGNED, + auth_authorizer_signature=CUSTOM_AUTHORIZER_SIGNATURE, + auth_password=CUSTOM_AUTHORIZER_PASSWORD, + auth_token_key_name=CUSTOM_AUTHORIZER_TOKEN_KEY_NAME, + auth_token_value=CUSTOM_AUTHORIZER_TOKEN_VALUE, + endpoint=CUSTOM_AUTHORIZER_ENDPOINT, + client_id=create_client_id(), + client_bootstrap=bootstrap) + + self._test_connection(connection) + diff --git a/test/test_mqtt5.py b/test/test_mqtt5.py index abc9e15f..d4e4bf37 100644 --- a/test/test_mqtt5.py +++ b/test/test_mqtt5.py @@ -16,6 +16,20 @@ PROXY_HOST = os.environ.get('proxyhost') PROXY_PORT = int(os.environ.get('proxyport', '0')) +CUSTOM_AUTHORIZER_ENDPOINT = os.environ.get("CUSTOM_AUTHORIZER_ENDPOINT") +CUSTOM_AUTHORIZER_NAME_SIGNED = os.environ.get("CUSTOM_AUTHORIZER_NAME_SIGNED") +CUSTOM_AUTHORIZER_NAME_UNSIGNED = os.environ.get("CUSTOM_AUTHORIZER_NAME_UNSIGNED") +CUSTOM_AUTHORIZER_PASSWORD = os.environ.get("CUSTOM_AUTHORIZER_PASSWORD") +CUSTOM_AUTHORIZER_SIGNATURE = os.environ.get("CUSTOM_AUTHORIZER_SIGNATURE") +CUSTOM_AUTHORIZER_TOKEN_KEY_NAME = os.environ.get("CUSTOM_AUTHORIZER_TOKEN_KEY_NAME") +CUSTOM_AUTHORIZER_TOKEN_VALUE = os.environ.get("CUSTOM_AUTHORIZER_TOKEN_VALUE") + + +def has_custom_auth_environment(): + return (CUSTOM_AUTHORIZER_ENDPOINT is not None) and (CUSTOM_AUTHORIZER_NAME_SIGNED is not None) and \ + (CUSTOM_AUTHORIZER_NAME_UNSIGNED is not None) and (CUSTOM_AUTHORIZER_PASSWORD is not None) and \ + (CUSTOM_AUTHORIZER_SIGNATURE is not None) and (CUSTOM_AUTHORIZER_TOKEN_KEY_NAME is not None) and \ + (CUSTOM_AUTHORIZER_TOKEN_VALUE is not None) class Config: cache = None @@ -198,4 +212,86 @@ def test_websockets_proxy(self): on_lifecycle_connection_success=callbacks.on_lifecycle_connection_success, on_lifecycle_stopped=callbacks.on_lifecycle_stopped) - self._test_connection(client, callbacks) \ No newline at end of file + self._test_connection(client, callbacks) + + @unittest.skipIf(not has_custom_auth_environment(), 'requires custom authentication env vars') + def test_mqtt5_builder_direct_signed_custom_authorizer(self): + elg = EventLoopGroup() + resolver = DefaultHostResolver(elg) + bootstrap = ClientBootstrap(elg, resolver) + callbacks = Mqtt5TestCallbacks() + + client = mqtt5_client_builder.direct_with_custom_authorizer( + auth_username="", + auth_authorizer_name=CUSTOM_AUTHORIZER_NAME_SIGNED, + auth_authorizer_signature=CUSTOM_AUTHORIZER_SIGNATURE, + auth_password=CUSTOM_AUTHORIZER_PASSWORD, + auth_token_key_name=CUSTOM_AUTHORIZER_TOKEN_KEY_NAME, + auth_token_value=CUSTOM_AUTHORIZER_TOKEN_VALUE, + endpoint=CUSTOM_AUTHORIZER_ENDPOINT, + client_id=create_client_id(), + client_bootstrap=bootstrap, + on_lifecycle_connection_success=callbacks.on_lifecycle_connection_success, + on_lifecycle_stopped=callbacks.on_lifecycle_stopped) + + self._test_connection(client, callbacks) + + @unittest.skipIf(not has_custom_auth_environment(), 'requires custom authentication env vars') + def test_mqtt5_builder_direct_unsigned_custom_authorizer(self): + elg = EventLoopGroup() + resolver = DefaultHostResolver(elg) + bootstrap = ClientBootstrap(elg, resolver) + callbacks = Mqtt5TestCallbacks() + + client = mqtt5_client_builder.direct_with_custom_authorizer( + auth_username="", + auth_authorizer_name=CUSTOM_AUTHORIZER_NAME_UNSIGNED, + auth_password=CUSTOM_AUTHORIZER_PASSWORD, + endpoint=CUSTOM_AUTHORIZER_ENDPOINT, + client_id=create_client_id(), + client_bootstrap=bootstrap, + on_lifecycle_connection_success=callbacks.on_lifecycle_connection_success, + on_lifecycle_stopped=callbacks.on_lifecycle_stopped) + + self._test_connection(client, callbacks) + + @unittest.skipIf(not has_custom_auth_environment(), 'requires custom authentication env vars') + def test_mqtt5_builder_websocket_signed_custom_authorizer(self): + elg = EventLoopGroup() + resolver = DefaultHostResolver(elg) + bootstrap = ClientBootstrap(elg, resolver) + callbacks = Mqtt5TestCallbacks() + + client = mqtt5_client_builder.websockets_with_custom_authorizer( + auth_username="", + auth_authorizer_name=CUSTOM_AUTHORIZER_NAME_SIGNED, + auth_authorizer_signature=CUSTOM_AUTHORIZER_SIGNATURE, + auth_password=CUSTOM_AUTHORIZER_PASSWORD, + auth_token_key_name=CUSTOM_AUTHORIZER_TOKEN_KEY_NAME, + auth_token_value=CUSTOM_AUTHORIZER_TOKEN_VALUE, + endpoint=CUSTOM_AUTHORIZER_ENDPOINT, + client_id=create_client_id(), + client_bootstrap=bootstrap, + on_lifecycle_connection_success=callbacks.on_lifecycle_connection_success, + on_lifecycle_stopped=callbacks.on_lifecycle_stopped) + + self._test_connection(client, callbacks) + + @unittest.skipIf(not has_custom_auth_environment(), 'requires custom authentication env vars') + def test_mqtt5_builder_websocket_unsigned_custom_authorizer(self): + elg = EventLoopGroup() + resolver = DefaultHostResolver(elg) + bootstrap = ClientBootstrap(elg, resolver) + callbacks = Mqtt5TestCallbacks() + + client = mqtt5_client_builder.websockets_with_custom_authorizer( + auth_username="", + auth_authorizer_name=CUSTOM_AUTHORIZER_NAME_UNSIGNED, + auth_password=CUSTOM_AUTHORIZER_PASSWORD, + endpoint=CUSTOM_AUTHORIZER_ENDPOINT, + client_id=create_client_id(), + client_bootstrap=bootstrap, + on_lifecycle_connection_success=callbacks.on_lifecycle_connection_success, + on_lifecycle_stopped=callbacks.on_lifecycle_stopped) + + self._test_connection(client, callbacks)