Skip to content

Commit

Permalink
Replace keyring package with secrets sdk
Browse files Browse the repository at this point in the history
Signed-off-by: Timothy Johnson <[email protected]>
  • Loading branch information
t1m0thyj committed Nov 3, 2023
1 parent 5eca570 commit a6e58c9
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 118 deletions.
69 changes: 36 additions & 33 deletions .github/workflows/sdk-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,44 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
env:
OS: ${{ matrix.os }}
PYTHON: ${{ matrix.python-version }}

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 ./src --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 ./src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
coverage run -m pytest ./tests/unit
- name: Generate a coverage xml file
run: |
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
if: ${{ matrix.python-version == '3.11' }}
with:
directory: ./
env_vars: OS,PYTHON
fail_ci_if_error: true
files: ./coverage.xml
flags: unittests
name: codecov-umbrella
verbose: true
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Build keyring binary
run: maturin develop
working-directory: src/secrets
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 ./src --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 ./src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
coverage run -m pytest ./tests/unit
- name: Generate a coverage xml file
run: |
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
if: ${{ matrix.python-version == '3.11' }}
with:
directory: ./
env_vars: OS,PYTHON
fail_ci_if_error: true
files: ./coverage.xml
flags: unittests
name: codecov-umbrella
verbose: true
4 changes: 2 additions & 2 deletions .github/workflows/secrets-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# maturin generate-ci github
#
name: CI
name: Secrets SDK CI

on:
push:
Expand Down Expand Up @@ -68,7 +68,7 @@ jobs:
- uses: actions/setup-python@v4
if: ${{ matrix.target != 'aarch64' }}
with:
python-version: "3.7"
python-version: "3.10"
architecture: ${{ matrix.target }}
- name: Build wheels
uses: PyO3/maturin-action@v1
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ idna==2.10
importlib-metadata==3.6.0
isort
jsonschema==4.17.3
keyring
lxml==4.9.3
maturin
mccabe==0.6.1
Expand Down
11 changes: 9 additions & 2 deletions src/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

from setuptools import find_namespace_packages, setup

sys.path.append("..")
sys.path.insert(0, "..")
from _version import __version__
from setup import resolve_sdk_dep

setup(
name="zowe_core_for_zowe_sdk",
Expand All @@ -18,6 +19,12 @@
"Programming Language :: Python :: 3.7",
"License :: OSI Approved :: Eclipse Public License 2.0 (EPL-2.0)",
],
install_requires=["requests", "urllib3", "pyyaml", "commentjson"],
install_requires=[
"commentjson",
"pyyaml",
"requests",
"urllib3",
resolve_sdk_dep("secrets", "~=0.1.0"),
],
packages=find_namespace_packages(include=["zowe.*"]),
)
22 changes: 3 additions & 19 deletions src/core/zowe/core_for_zowe_sdk/credential_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@

HAS_KEYRING = True
try:
import keyring

from zowe.secrets_for_zowe_sdk import keyring
except ImportError:
HAS_KEYRING = False

Expand Down Expand Up @@ -76,11 +75,8 @@ def _retrieve_credential(service_name: str) -> Optional[str]:
"""
# Configure the logger to ignore warning messages
logging.getLogger().setLevel(logging.ERROR)
is_win32 = sys.platform == "win32"
if is_win32:
service_name += "/" + constants["ZoweAccountName"]
encoded_credential = keyring.get_password(service_name, constants["ZoweAccountName"])
if encoded_credential is None and is_win32:
if encoded_credential is None and sys.platform == "win32":
# Retrieve the secure value with an index
index = 1
temp_value = keyring.get_password(f"{service_name}-{index}", f"{constants['ZoweAccountName']}-{index}")
Expand All @@ -92,16 +88,6 @@ def _retrieve_credential(service_name: str) -> Optional[str]:
index += 1
temp_value = keyring.get_password(f"{service_name}-{index}", f"{constants['ZoweAccountName']}-{index}")

if is_win32:
try:
encoded_credential = encoded_credential.encode("utf-16le").decode()
except (UnicodeDecodeError, AttributeError):
# The credential is not encoded in UTF-16
pass

if encoded_credential is not None and encoded_credential.endswith("\0"):
encoded_credential = encoded_credential[:-1]

return encoded_credential

@staticmethod
Expand Down Expand Up @@ -156,7 +142,6 @@ def save_secure_props() -> None:

encoded_credential = base64.b64encode(commentjson.dumps(credential).encode()).decode()
if is_win32:
service_name += "/" + constants["ZoweAccountName"]
# Delete the existing credential
CredentialManager.delete_credential(service_name, constants["ZoweAccountName"])
# Check if the encoded credential exceeds the maximum length for win32
Expand All @@ -167,9 +152,8 @@ def save_secure_props() -> None:
chunks = [encoded_credential[i : i + chunk_size] for i in range(0, len(encoded_credential), chunk_size)]
# Set the individual chunks as separate keyring entries
for index, chunk in enumerate(chunks, start=1):
password = (chunk + "\0" * (len(chunk) % 2)).encode().decode("utf-16le")
field_name = f"{constants['ZoweAccountName']}-{index}"
keyring.set_password(f"{service_name}-{index}", field_name, password)
keyring.set_password(f"{service_name}-{index}", field_name, chunk)

else:
# Credential length is within the maximum limit or not on win32, set it as a single keyring entry
Expand Down
43 changes: 1 addition & 42 deletions src/core/zowe/core_for_zowe_sdk/zosmf_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

HAS_KEYRING = True
try:
import keyring
from zowe.secrets_for_zowe_sdk import keyring
except ImportError:
HAS_KEYRING = False

Expand Down Expand Up @@ -94,18 +94,12 @@ def __get_secure_value(self, name):
service_name = constants["ZoweCredentialKey"]
account_name = "zosmf_{}_{}".format(self.profile_name, name)

if sys.platform == "win32":
service_name += "/" + account_name

secret_value = keyring.get_password(service_name, account_name)

# Handle the case when secret_value is None
if secret_value is None:
secret_value = ""

if sys.platform == "win32":
secret_value = secret_value.encode("utf-16")

secret_value = base64.b64decode(secret_value).decode().strip('"')

return secret_value
Expand All @@ -122,38 +116,3 @@ def __load_secure_credentials(self):
raise SecureProfileLoadFailed(self.profile_name, e)
else:
return (zosmf_user, zosmf_password)


if HAS_KEYRING and sys.platform.startswith("linux"):
from contextlib import closing

from keyring.backends import SecretService

class KeyringBackend(SecretService.Keyring):
"""
Class used to handle secured profiles.
Methods
-------
get_password(service, username)
Get the decoded password
"""

def __get_password(self, service, username, collection):
items = collection.search_items({"account": username, "service": service})
for item in items:
if hasattr(item, "unlock"):
if item.is_locked() and item.unlock()[0]:
raise keyring.errors.InitError("failed to unlock item")
return item.get_secret().decode("utf-8")

def get_password(self, service, username):
"""Get password of the username for the service."""
collection = self.get_preferred_collection()
if hasattr(collection, "connection"):
with closing(collection.connection):
return self.__get_password(service, username, collection)
else:
return self.__get_password(service, username, collection)

keyring.set_keyring(KeyringBackend())
Loading

0 comments on commit a6e58c9

Please sign in to comment.