Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

Release History
===============
0.10.7
+++++++++++++++

**IoT Hub updates**

* Breaking Change - removes and replaces "az iot hub device-identity regenerate-key" with the new "az iot hub device-identity regenerate-keys".

0.10.6
+++++++++++++++

Expand Down
12 changes: 6 additions & 6 deletions azext_iot/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,15 @@
"""

helps[
"iot hub device-identity regenerate-key"
"iot hub device-identity regenerate-keys"
] = """
type: command
short-summary: Regenerate target keys of an IoT Hub device with sas authentication.
short-summary: Regenerate keys of an IoT Hub device with symmetric key authentication.
examples:
- name: Regenerate the primary key.
text: az iot hub device-identity regenerate-key -d {device_id} -n {iothub_name} --kt primary
- name: Swap the primary and secondary keys.
text: az iot hub device-identity regenerate-key -d {device_id} -n {iothub_name} --kt swap
- name: Regenerates the primary and secondary keys.
text: az iot hub device-identity regenerate-keys -d {device_id} -n {iothub_name}
- name: Swaps the primary and secondary keys. No key generation occurs.
text: az iot hub device-identity regenerate-keys -d {device_id} -n {iothub_name} --swap
"""

helps[
Expand Down
11 changes: 5 additions & 6 deletions azext_iot/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
JobCreateType,
JobStatusType,
AuthenticationType,
RegenerateKeyType,
)
from azext_iot._validators import mode2_iot_login_handler
from azext_iot.assets.user_messages import info_param_properties_device
Expand Down Expand Up @@ -416,12 +415,12 @@ def load_arguments(self, _):
deprecate_info=context.deprecate()
)

with self.argument_context('iot hub device-identity regenerate-key') as context:
with self.argument_context('iot hub device-identity regenerate-keys') as context:
context.argument(
"regenerate_key",
options_list=["--key-type", "--kt"],
arg_type=get_enum_type(RegenerateKeyType),
help="Target key type to regenerate."
"swap",
options_list=["--swap"],
arg_type=get_three_state_flag(),
help="Flag indicating the operation should swap primary and secondary keys only. No new keys will be generated. ",
)

with self.argument_context("iot hub device-identity export") as context:
Expand Down
2 changes: 1 addition & 1 deletion azext_iot/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def load_command_table(self, _):
setter_name="iot_device_update",
custom_func_name="update_iot_device_custom"
)
cmd_group.command("regenerate-key", 'iot_device_key_regenerate')
cmd_group.command("regenerate-keys", "iot_device_keys_regenerate")
cmd_group.command(
"show-connection-string",
"iot_get_device_connection_string",
Expand Down
10 changes: 0 additions & 10 deletions azext_iot/common/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,16 +216,6 @@ class AuthenticationType(Enum):
identityBased = "identity"


class RegenerateKeyType(Enum):
"""
Target key type for regeneration.
"""

primary = KeyType.primary.value
secondary = KeyType.secondary.value
swap = "swap"


class IoTHubStateType(Enum):
"""
IoT Hub State Property
Expand Down
9 changes: 0 additions & 9 deletions azext_iot/common/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import re
import hmac
import hashlib
import random

from threading import Event, Thread
from datetime import datetime
Expand Down Expand Up @@ -516,11 +515,3 @@ def compute_device_key(primary_key, registration_id):
).digest()
)
return device_key


def generate_key(byte_length=32):
key = ""
while byte_length > 0:
key += chr(random.randrange(1, 128))
byte_length -= 1
return base64.b64encode(key.encode()).decode("utf-8")
2 changes: 1 addition & 1 deletion azext_iot/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import os

VERSION = "0.10.6"
VERSION = "0.10.7"
EXTENSION_NAME = "azure-iot"
EXTENSION_ROOT = os.path.dirname(os.path.abspath(__file__))
EXTENSION_CONFIG_ROOT_KEY = "iotext"
Expand Down
43 changes: 18 additions & 25 deletions azext_iot/operations/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
ConfigType,
KeyType,
SettleType,
RegenerateKeyType,
IoTHubStateType
)
from azext_iot.iothub.providers.discovery import IotHubDiscovery
Expand All @@ -37,7 +36,6 @@
init_monitoring,
process_json_arg,
ensure_min_version,
generate_key
)
from azext_iot._factory import SdkResolver, CloudError
from azext_iot.operations.generic import _execute_query, _process_top
Expand Down Expand Up @@ -401,43 +399,38 @@ def _update_device_key(target, device, auth_method, pk, sk):
try:
auth = _assemble_auth(auth_method, pk, sk)
device["authentication"] = auth
etag = device.get("etag", None)
if etag:
headers = {}
headers["If-Match"] = '"{}"'.format(etag)
return service_sdk.devices.create_or_update_identity(
id=device["deviceId"],
device=device,
custom_headers=headers,
)
raise LookupError("device etag not found.")
etag = device.get("etag", "*")
headers = {}
headers["If-Match"] = '"{}"'.format(etag)
return service_sdk.devices.create_or_update_identity(
id=device["deviceId"],
device=device,
custom_headers=headers,
)
except CloudError as e:
raise CLIError(unpack_msrest_error(e))
except LookupError as err:
raise CLIError(err)


def iot_device_key_regenerate(cmd, hub_name, device_id, regenerate_key, resource_group_name=None, login=None):
def iot_device_keys_regenerate(cmd, hub_name, device_id, swap=False, resource_group_name=None, login=None):
discovery = IotHubDiscovery(cmd)
target = discovery.get_target(
hub_name=hub_name, resource_group_name=resource_group_name, login=login
)
device = _iot_device_show(target, device_id)
if (device["authentication"]["type"] != "sas"):
raise CLIError("Device authentication should be of type sas")

pk = device["authentication"]["symmetricKey"]["primaryKey"]
sk = device["authentication"]["symmetricKey"]["secondaryKey"]
if regenerate_key == RegenerateKeyType.primary.value:
pk = generate_key()
if regenerate_key == RegenerateKeyType.secondary.value:
sk = generate_key()
if regenerate_key == RegenerateKeyType.swap.value:
raise CLIError("Device authentication must be of type sas (symmetricKey).")

if swap:
pk = device["authentication"]["symmetricKey"]["primaryKey"]
sk = device["authentication"]["symmetricKey"]["secondaryKey"]

temp = pk
pk = sk
sk = temp

return _update_device_key(target, device, device["authentication"]["type"], pk, sk)
return _update_device_key(target, device, device["authentication"]["type"], pk, sk)

return _update_device_key(target, device, device["authentication"]["type"], "", "")


def iot_device_get_parent(
Expand Down
17 changes: 7 additions & 10 deletions azext_iot/tests/test_iot_ext_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,21 +518,19 @@ def test_hub_devices(self):
],
)

# Test 'az iot hub device regenerate-key'
# Test 'az iot hub device regenerate-keys'
device = self.cmd(
'''iot hub device-identity regenerate-key -d {} -n {} -g {} --kt primary
'''.format(
'''iot hub device-identity regenerate-keys -d {} -n {} -g {}'''.format(
edge_device_ids[1], LIVE_HUB, LIVE_RG
),
checks=[
self.check("deviceId", edge_device_ids[1])
]
).get_output_in_json()

# Test swap keys 'az iot hub device regenerate-key'
# Test swap keys 'az iot hub device regenerate-keys'
self.cmd(
'''iot hub device-identity regenerate-key -d {} -n {} -g {} --kt swap
'''.format(
'''iot hub device-identity regenerate-keys -d {} -n {} -g {} --swap'''.format(
edge_device_ids[1], LIVE_HUB, LIVE_RG
),
checks=[
Expand All @@ -541,10 +539,9 @@ def test_hub_devices(self):
],
)

# Test 'az iot hub device regenerate-key' with non sas authentication
self.cmd("iot hub device-identity regenerate-key -d {} -n {} -g {} --kt secondary"
.format(device_ids[0], LIVE_HUB, LIVE_RG),
expect_failure=True)
# Test 'az iot hub device regenerate-keys' with non sas authentication
self.cmd("iot hub device-identity regenerate-keys -d {} -n {} -g {}".format(
device_ids[0], LIVE_HUB, LIVE_RG), expect_failure=True)

sym_conn_str_pattern = r"^HostName={}\.azure-devices\.net;DeviceId={};SharedAccessKey=".format(
LIVE_HUB, edge_device_ids[0]
Expand Down
48 changes: 21 additions & 27 deletions azext_iot/tests/test_iot_ext_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ def test_device_update_error(self, serviceclient_generic_error, req):
)


class TestDeviceRegenerateKey:
class TestDeviceRegenerateKeys:
@pytest.fixture(params=[200])
def serviceclient(self, mocker, fixture_ghcs, fixture_sas, request):
service_client = mocker.patch(path_service_client)
Expand All @@ -662,10 +662,10 @@ def serviceclient(self, mocker, fixture_ghcs, fixture_sas, request):
service_client.side_effect = test_side_effect
return service_client

@pytest.mark.parametrize("req", ["primary", "secondary", "swap"])
def test_device_key_regenerate(self, fixture_cmd, serviceclient, req):
subject.iot_device_key_regenerate(
fixture_cmd, mock_target["entity"], device_id, req
@pytest.mark.parametrize("swap", [False, True])
def test_device_keys_regenerate(self, fixture_cmd, serviceclient, swap):
subject.iot_device_keys_regenerate(
fixture_cmd, mock_target["entity"], device_id, swap=swap
)
args = serviceclient.call_args
assert (
Expand All @@ -674,16 +674,17 @@ def test_device_key_regenerate(self, fixture_cmd, serviceclient, req):
assert args[0][0].method == "PUT"

body = json.loads(args[0][0].body)
if(req == "primary"):
assert body["authentication"]["symmetricKey"]["primaryKey"] != "123"
if(req == "secondary"):
assert body["authentication"]["symmetricKey"]["secondaryKey"] != "321"
if(req == "swap"):

if(swap):
assert body["authentication"]["symmetricKey"]["primaryKey"] == "321"
assert body["authentication"]["symmetricKey"]["secondaryKey"] == "123"
return

assert body["authentication"]["symmetricKey"]["primaryKey"] == ""
assert body["authentication"]["symmetricKey"]["secondaryKey"] == ""

@pytest.fixture(params=[200])
def serviceclient_invalid_args(self, mocker, fixture_ghcs, fixture_sas, request):
def serviceclient_invalid_auth(self, mocker, fixture_ghcs, fixture_sas, request):
service_client = mocker.patch(path_service_client)
kvp = {}
kvp.setdefault("authentication", {"type": "test"})
Expand All @@ -693,25 +694,18 @@ def serviceclient_invalid_args(self, mocker, fixture_ghcs, fixture_sas, request)
service_client.side_effect = test_side_effect
return service_client

@pytest.mark.parametrize(
"req, exp",
[
("primary", CLIError),
("secondary", CLIError),
("swap", CLIError)
]
)
def test_device_key_regenerate_invalid_args(self, fixture_cmd, serviceclient_invalid_args, req, exp):
with pytest.raises(exp):
subject.iot_device_key_regenerate(
fixture_cmd, mock_target["entity"], device_id, req
@pytest.mark.parametrize("swap", [False, True])
def test_device_keys_regenerate_invalid_auth(self, serviceclient_invalid_auth, swap):
with pytest.raises(CLIError):
subject.iot_device_keys_regenerate(
fixture_cmd, mock_target["entity"], device_id, swap=swap
)

@pytest.mark.parametrize("req", ["primary", "secondary", "swap"])
def test_device_key_regenerate_error(self, serviceclient_generic_error, req):
@pytest.mark.parametrize("swap", [False, True])
def test_device_keys_regenerate_error(self, serviceclient_generic_error, swap):
with pytest.raises(CLIError):
subject.iot_device_key_regenerate(
fixture_cmd, mock_target["entity"], device_id, req
subject.iot_device_keys_regenerate(
fixture_cmd, mock_target["entity"], device_id, swap=swap
)


Expand Down