Skip to content

Commit

Permalink
Release Python SDK v16.6.6 (#649)
Browse files Browse the repository at this point in the history
* KSM-552 Stop generating UIDs that start with "-" (#648)

* KSM-554 Added new and updated PAM field types (#653)

* Bumping helper version to 1.0.5
  • Loading branch information
idimov-keeper authored Sep 18, 2024
1 parent f7ec4fb commit 1d13302
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 35 deletions.
5 changes: 4 additions & 1 deletion sdk/python/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ For more information see our official documentation page https://docs.keeper.io/

# Change Log

## 16.6.6
* KSM-552 - Stop generating UIDs that start with "-"

## 16.6.5
* KSM-529 - Hande broken encryption in records and files
* KSM-529 - Handle broken encryption in records and files

## 16.6.4
* KSM-488 - Remove unused package dependencies
Expand Down
31 changes: 19 additions & 12 deletions sdk/python/core/keeper_secrets_manager_core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,22 @@
from keeper_secrets_manager_core import utils, helpers
from keeper_secrets_manager_core.configkeys import ConfigKeys
from keeper_secrets_manager_core.crypto import CryptoUtils
from keeper_secrets_manager_core.dto.dtos import Folder, Record, RecordCreate, SecretsManagerResponse, AppData, \
from keeper_secrets_manager_core.dto.dtos import Folder, Record, \
RecordCreate, SecretsManagerResponse, AppData, \
KeeperFileUpload, KeeperFile, KeeperFolder
from keeper_secrets_manager_core.dto.payload import CompleteTransactionPayload, GetPayload, UpdatePayload, TransmissionKey, \
EncryptedPayload, KSMHttpResponse, CreatePayload, FileUploadPayload, DeletePayload, \
CreateFolderPayload, UpdateFolderPayload, DeleteFolderPayload, CreateOptions, QueryOptions
from keeper_secrets_manager_core.dto.payload import GetPayload, \
CompleteTransactionPayload, UpdatePayload, TransmissionKey, \
EncryptedPayload, KSMHttpResponse, CreatePayload, FileUploadPayload, \
DeletePayload, CreateFolderPayload, UpdateFolderPayload, \
DeleteFolderPayload, CreateOptions, QueryOptions
from keeper_secrets_manager_core.exceptions import KeeperError
from keeper_secrets_manager_core.keeper_globals import keeper_secrets_manager_sdk_client_id, keeper_public_keys, \
logger_name, keeper_servers
from keeper_secrets_manager_core.storage import FileKeyValueStorage, KeyValueStorage, InMemoryKeyValueStorage
from keeper_secrets_manager_core.keeper_globals import keeper_public_keys, \
keeper_secrets_manager_sdk_client_id, logger_name, keeper_servers
from keeper_secrets_manager_core.storage import FileKeyValueStorage, \
KeyValueStorage, InMemoryKeyValueStorage
from keeper_secrets_manager_core.utils import base64_to_bytes, dict_to_json, \
url_safe_str_to_bytes, bytes_to_base64, generate_random_bytes, now_milliseconds, string_to_bytes, json_to_dict, \
url_safe_str_to_bytes, bytes_to_base64, generate_random_bytes, \
generate_uid_bytes, now_milliseconds, string_to_bytes, json_to_dict, \
bytes_to_string, strtobool


Expand All @@ -42,6 +47,7 @@ def find_secrets_by_title(record_title, records):
records = records or []
return [x for x in records if x.title == record_title]


def find_secret_by_title(record_title, records):
# Find first record with specified title
records = records or []
Expand All @@ -60,6 +66,7 @@ def __init__(self, section: str):
self.index1: Optional[Tuple[str, str]] = None # numeric index [N] or []
self.index2: Optional[Tuple[str, str]] = None # property index - ex. field/name[0][middle]


class SecretsManager:

notation_prefix = "keeper"
Expand Down Expand Up @@ -362,7 +369,7 @@ def prepare_create_payload(storage, create_options: CreateOptions, record_data_j
raise KeeperError('Unable to create record - folder key for ' + create_options.folder_uid + ' is missing')

record_key = generate_random_bytes(32)
record_uid = generate_random_bytes(16)
record_uid = generate_uid_bytes()

record_data_bytes = utils.string_to_bytes(record_data_json_str)
record_data_encrypted = CryptoUtils.encrypt_aes(record_data_bytes, record_key)
Expand Down Expand Up @@ -496,15 +503,15 @@ def prepare_create_folder_payload(storage, create_options, folder_name, shared_f
payload.sharedFolderUid = create_options.folder_uid
payload.parentUid = create_options.subfolder_uid

folder_uid = utils.generate_random_bytes(16)
folder_uid = generate_uid_bytes()
payload.folderUid = CryptoUtils.bytes_to_url_safe_str(folder_uid)

folder_key = utils.generate_random_bytes(32)
folder_key = generate_random_bytes(32)
encrypted_folder_key = CryptoUtils.encrypt_aes_cbc(folder_key, shared_folder_key)
payload.sharedFolderKey = CryptoUtils.bytes_to_url_safe_str(encrypted_folder_key)

folder_json = dict_to_json({"name": folder_name})
folder_data_bytes = utils.string_to_bytes(folder_json)
folder_data_bytes = string_to_bytes(folder_json)
encrypted_folder_data = CryptoUtils.encrypt_aes_cbc(folder_data_bytes, folder_key)
payload.data = CryptoUtils.bytes_to_url_safe_str(encrypted_folder_data)

Expand Down
1 change: 1 addition & 0 deletions sdk/python/core/keeper_secrets_manager_core/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def unpad_data(data): # type: (bytes) -> bytes
unpadder = PKCS7(16*8).unpadder()
return unpadder.update(data) + unpadder.finalize()


class CryptoUtils:

BS = 16
Expand Down
16 changes: 11 additions & 5 deletions sdk/python/core/keeper_secrets_manager_core/dto/dtos.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,11 +454,17 @@ def __init__(self):
self.type = None


VALID_RECORD_FIELDS = ['login', 'password', 'url', 'fileRef', 'oneTimeCode', 'otp', 'name', 'birthDate', 'date',
'expirationDate', 'text', 'securityQuestion', 'multiline', 'email', 'cardRef', 'addressRef',
'pinCode', 'phone', 'secret', 'note', 'accountNumber', 'paymentCard', 'bankAccount', 'keyPair',
'host', 'address', 'licenseNumber', 'recordRef', 'schedule', 'directoryType', 'databaseType',
'pamHostname', 'pamResources', 'checkbox', 'passkey', 'script']
VALID_RECORD_FIELDS = [
'accountNumber', 'address', 'addressRef', 'appFiller', 'bankAccount',
'birthDate', 'cardRef', 'checkbox', 'databaseType', 'date',
'directoryType', 'dropdown', 'email', 'expirationDate', 'fileRef', 'host',
'isSSIDHidden', 'keyPair', 'licenseNumber', 'login', 'multiline', 'name',
'note', 'oneTimeCode', 'otp', 'pamHostname', 'pamRemoteBrowserSettings',
'pamResources', 'pamSettings', 'passkey', 'password', 'paymentCard',
'phone', 'pinCode', 'rbiUrl', 'recordRef', 'schedule', 'script', 'secret',
'securityQuestion', 'text', 'trafficEncryptionSeed', 'url',
'wifiEncryption'
]


class RecordField:
Expand Down
12 changes: 12 additions & 0 deletions sdk/python/core/keeper_secrets_manager_core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ def generate_random_bytes(length):
return os.urandom(length)


def generate_uid_bytes():
dash = bytes(b'\xf8\x7f') # 11111000, 0b01111111
uid_bytes = bytes()
for _ in range(8):
uid_bytes = generate_random_bytes(16)
if dash[0] & uid_bytes[0] != dash[0]:
break
if dash[0] & uid_bytes[0] == dash[0]:
uid_bytes = bytes([uid_bytes[0] & dash[1]]) + uid_bytes[1:]
return uid_bytes


def dict_to_json(dictionary):
return json.dumps(dictionary, indent=4)

Expand Down
2 changes: 1 addition & 1 deletion sdk/python/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

setup(
name="keeper-secrets-manager-core",
version="16.6.5",
version="16.6.6",
description="Keeper Secrets Manager for Python 3",
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down
118 changes: 112 additions & 6 deletions sdk/python/helper/keeper_secrets_manager_helper/v3/field_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ class Multiline(FieldType):
name = "multiline"


# "file" - obsolete and removed legacy field - "fldt_file": { key: 'file_or_photo', default: "File or Photo" },
class FileRef(FieldType):
name = "fileRef"
# The validation checks to see if value is a Record UID.
Expand Down Expand Up @@ -678,16 +679,12 @@ class Host(FieldType):
}
}

# AppFiller?


class LicenseNumber(FieldType):
name = "licenseNumber"
schema = {"value_type": str, "desc": "License Number"}


# privateKey?

class SecureNote(FieldType):
name = "note"
schema = {"value_type": str, "desc": "Secret Note"}
Expand All @@ -705,7 +702,10 @@ class Schedule(FieldType):
"value_type": dict,
"schema": {
"type": {"value_type": str, "desc": "Type"},
"utcTime": {"value_type": str, "desc": "UTC Timestamp"},
"cron": {"value_type": str, "desc": "Crontab format string"},
# "utcTime" - replaced by time and tz
"time": {"value_type": str, "desc": "Time"},
"tz": {"value_type": str, "desc": "Time zone"},
"weekday": {"value_type": str, "desc": "Day of the Week"},
"intervalCount": {"value_type": int, "desc": "Interval Count"}
}
Expand Down Expand Up @@ -740,7 +740,16 @@ class PamResources(FieldType):
"schema": {
"controllerUid": {"value_type": str, "desc": "Record UID of the Controller Record"},
"folderUid": {"value_type": str, "desc": "Folder UID"},
"resourceRef": {"value_type": list, "desc": "List with UIDs of resource records"}
"resourceRef": {"value_type": list, "desc": "List with UIDs of resource records"},
"allowedSettings": {
"value_type": dict, "desc": "List with allowed settings flags",
"schema": {
"connections": {"value_type": bool},
"portForwards": {"value_type": bool},
"rotation": {"value_type": bool},
"sessionRecording": {"value_type": bool},
"typescriptRecording": {"value_type": bool}
}},
}
}

Expand Down Expand Up @@ -774,6 +783,7 @@ class Passkey(FieldType):
}
}


class Script(FieldType):
name = "script"
schema = {
Expand All @@ -784,3 +794,99 @@ class Script(FieldType):
"recordRef": {"value_type": str, "validate": UID_REGEX, "desc": "Record UID of the referenced record."}
}
}


class IsSSIDHidden(FieldType):
name = "isSSIDHidden"
schema = {"value_type": bool, "desc": "Is SSID Hidden"}


class WifiEncryption(FieldType):
name = "wifiEncryption"


class Dropdown(FieldType):
name = "dropdown"


class RbiUrl(FieldType):
name = "rbiUrl"


class AppFiller(FieldType):
name = "appFiller"
schema = {
"value_type": dict,
"schema": {
"applicationTitle": {"value_type": str, "desc": "Application Title"},
"contentFilter": {"value_type": str, "desc": "Content Filter"},
"macroSequence": {"value_type": str, "desc": "Macro Sequence"},
}
}


class PamRemoteBrowserSettings(FieldType):
name = "pamRemoteBrowserSettings"
schema = {
"value_type": dict,
"schema": {
"connection": {
"value_type": dict,
"desc": "Connection details",
"schema": {
"protocol": {"value_type": str},
"userRecords": {"value_type": list},
"allowUrlManipulation": {"value_type": bool},
"allowedUrlPatterns": {"value_type": str},
"allowedResourceUrlPatterns": {"value_type": str},
"httpCredentialsUid": {"value_type": str},
"autofillConfiguration": {"value_type": str}
}
},
}
}


class PamSettings(FieldType):
name = "pamSettings"
schema = {
"value_type": dict,
"schema": {
"connection": {
"value_type": dict,
"desc": "PAM Settings",
"schema": {
"portForward": {
"value_type": list,
"desc": "Port Settings",
"schema": {
"reusePort": {"value_type": bool},
"port": {"value_type": str},
}
},
"connection": {
"value_type": list,
"desc": "Connection Settings",
"schema": {
"protocol": {"value_type": str},
"userRecords": {"value_type": list},
"security": {"value_type": str},
"ignoreCert": {"value_type": bool},
"resizeMethod": {"value_type": str},
"colorScheme": {"value_type": str}
}
},
}
},
}
}


class TrafficEncryptionSeed(FieldType):
name = "trafficEncryptionSeed"


# List of retired field types:
# trafficEncryptionKey - replaced by trafficEncryptionSeed
# pamProvider - deprecated for legacy/internal use only
# controller - deprecated for legacy/internal use only
2 changes: 1 addition & 1 deletion sdk/python/helper/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
keeper-secrets-manager-core>=16.2.2
keeper-secrets-manager-core>=16.6.6
pyyaml>=6.0.1
iso8601
2 changes: 1 addition & 1 deletion sdk/python/helper/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

setup(
name="keeper-secrets-manager-helper",
version="1.0.4",
version="1.0.5",
description="Keeper Secrets Manager SDK helper for managing records.",
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down
31 changes: 25 additions & 6 deletions sdk/python/helper/tests/v3/v3_field_type_all_fields_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def test_address(self):
a.street2 = "Apt B"
a.city = "Gotham"
a.zip = "11111-2222"
a.country = "CA"
a.country = CountryEnum.CA
self._check_dict(a, value={
"street1": "North Main Street",
"street2": "Apt B",
Expand Down Expand Up @@ -315,10 +315,11 @@ def test_record_ref(self):
def test_schedule(self):
ft = Schedule()
ft.type = "WEEKLY"
ft.utcTime = "00:00"
ft.time = "00:00:00"
ft.tz = "America/Chicago"
ft.weekday = "WEDNESDAY"
ft.intervalCount = 1
self._check_dict(ft, value={"type": "WEEKLY", "utcTime": "00:00", "weekday": "WEDNESDAY", "intervalCount": 1})
self._check_dict(ft, value={"type": "WEEKLY", "time": "00:00:00", "tz": "America/Chicago", "weekday": "WEDNESDAY", "intervalCount": 1})

def test_directory_type(self):
ft = DirectoryType()
Expand All @@ -341,7 +342,25 @@ def test_pam_resources(self):
ft.controllerUid = "OlLZ6JLjnyMOS3CiIPHBjw"
ft.folderUid = "so5ja6A46Zmr9J1QyCc06g"
ft.resourceRef = ["hUrGHrcM0PI3Y6Ch5wCrAQ"]
self._check_dict(ft, value={"controllerUid": "OlLZ6JLjnyMOS3CiIPHBjw", "folderUid": "so5ja6A46Zmr9J1QyCc06g", "resourceRef": ["hUrGHrcM0PI3Y6Ch5wCrAQ"]})
ft.allowedSettings = {
"connections": True,
"portForwards": True,
"rotation": True,
"sessionRecording": True,
"typescriptRecording": True
}
self._check_dict(ft, value={
"controllerUid": "OlLZ6JLjnyMOS3CiIPHBjw",
"folderUid": "so5ja6A46Zmr9J1QyCc06g",
"resourceRef": ["hUrGHrcM0PI3Y6Ch5wCrAQ"],
"allowedSettings": {
"connections": True,
"portForwards": True,
"rotation": True,
"sessionRecording": True,
"typescriptRecording": True
}
})

def test_checkbox(self):
ft = Checkbox()
Expand All @@ -367,7 +386,7 @@ def test_passkey(self):
ft.createdDate = 1625140800000
self._check_dict(ft, value={
"privateKey": {
"crv":"CRV",
"crv": "CRV",
"d": "DDDDD",
"ext": False,
"key_ops": [],
Expand All @@ -382,7 +401,7 @@ def test_passkey(self):
"username": "user1",
"createdDate": 1625140800000})

def test_scrpt(self):
def test_script(self):
ft = Script()
ft.fileRef = "OlLZ6JLjnyMOS3CiIPHBjw"
ft.command = "/bin/zsh"
Expand Down
Loading

0 comments on commit 1d13302

Please sign in to comment.