diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1c440b4..3a51f36 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,18 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v2.10.0 + rev: v2.34.0 hooks: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/psf/black - rev: stable + rev: 22.3.0 hooks: - id: black args: - --safe - --quiet - repo: https://github.com/codespell-project/codespell - rev: v2.0.0 + rev: v2.1.0 hooks: - id: codespell args: @@ -21,14 +21,14 @@ repos: - --quiet-level=2 exclude_types: [csv, json] - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + rev: 3.9.2 hooks: - id: flake8 additional_dependencies: - flake8-docstrings==1.5.0 - pydocstyle==5.1.1 - repo: https://github.com/PyCQA/bandit - rev: 1.7.0 + rev: 1.7.4 hooks: - id: bandit args: @@ -36,21 +36,21 @@ repos: - --format=custom - --configfile=tests/bandit.yaml - repo: https://github.com/PyCQA/isort - rev: 5.7.0 + rev: 5.10.1 hooks: - id: isort args: ["--profile", "black"] - repo: https://github.com/adrienverge/yamllint.git - rev: v1.26.0 + rev: v1.26.3 hooks: - id: yamllint - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.2.1 + rev: v2.7.1 hooks: - id: prettier stages: [manual] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.3.0 hooks: - id: check-executables-have-shebangs stages: [manual] @@ -59,3 +59,15 @@ repos: - id: no-commit-to-branch args: - --branch=master + - repo: local + hooks: + - id: pylint + name: pylint + entry: pylint + language: system + types: [python] + args: + [ + "-rn", # Only display messages + "-sn", # Don't display the score + ] diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..23d571f --- /dev/null +++ b/.pylintrc @@ -0,0 +1,611 @@ +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, checkers without error messages are disabled and for others, +# only the ERROR messages are displayed, and no reports are done by default. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold to be exceeded before program exits with error. +fail-under=10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the ignore-list. The +# regex matches against paths and can be in Posix or Windows format. +ignore-paths= + +# Files or directories matching the regex patterns are skipped. The regex +# matches against base names, not paths. The default value ignores Emacs file +# locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.9 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format= + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + too-many-instance-attributes, + too-many-arguments, + too-many-branches + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the 'python-enchant' package. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +notes-rgx= + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules= + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=BaseException, + Exception + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=yes + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[DESIGN] + +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= + +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= diff --git a/pyhiveapi/apyhiveapi/__init__.py b/pyhiveapi/apyhiveapi/__init__.py index 788a9bf..7436221 100644 --- a/pyhiveapi/apyhiveapi/__init__.py +++ b/pyhiveapi/apyhiveapi/__init__.py @@ -1,4 +1,5 @@ """__init__.py.""" +# pylint: skip-file if __name__ == "pyhiveapi": from .api.hive_api import HiveApi as API # noqa: F401 from .api.hive_auth import HiveAuth as Auth # noqa: F401 diff --git a/pyhiveapi/apyhiveapi/action.py b/pyhiveapi/apyhiveapi/action.py index 43bdadf..c4af98f 100644 --- a/pyhiveapi/apyhiveapi/action.py +++ b/pyhiveapi/apyhiveapi/action.py @@ -1,4 +1,5 @@ """Hive Action Module.""" +# pylint: skip-file class HiveAction: diff --git a/pyhiveapi/apyhiveapi/alarm.py b/pyhiveapi/apyhiveapi/alarm.py index 9e089dd..d85fdfb 100644 --- a/pyhiveapi/apyhiveapi/alarm.py +++ b/pyhiveapi/apyhiveapi/alarm.py @@ -1,4 +1,5 @@ """Hive Alarm Module.""" +# pylint: skip-file class HiveHomeShield: diff --git a/pyhiveapi/apyhiveapi/api/hive_api.py b/pyhiveapi/apyhiveapi/api/hive_api.py index 68beb5a..c81e282 100644 --- a/pyhiveapi/apyhiveapi/api/hive_api.py +++ b/pyhiveapi/apyhiveapi/api/hive_api.py @@ -1,4 +1,5 @@ """Hive API Module.""" +# pylint: skip-file import json import requests diff --git a/pyhiveapi/apyhiveapi/api/hive_async_api.py b/pyhiveapi/apyhiveapi/api/hive_async_api.py index 4aa06e4..c8018a1 100644 --- a/pyhiveapi/apyhiveapi/api/hive_async_api.py +++ b/pyhiveapi/apyhiveapi/api/hive_async_api.py @@ -1,5 +1,5 @@ """Hive API Module.""" - +# pylint: skip-file import json import operator from typing import Optional diff --git a/pyhiveapi/apyhiveapi/api/hive_auth.py b/pyhiveapi/apyhiveapi/api/hive_auth.py index 28794fb..424ac87 100644 --- a/pyhiveapi/apyhiveapi/api/hive_auth.py +++ b/pyhiveapi/apyhiveapi/api/hive_auth.py @@ -1,5 +1,5 @@ """Sync version of HiveAuth.""" - +# pylint: skip-file import base64 import binascii import datetime @@ -22,7 +22,7 @@ from .hive_api import HiveApi # https://github.com/aws/amazon-cognito-identity-js/blob/master/src/AuthenticationHelper.js#L22 -n_hex = ( +N_HEX = ( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" @@ -41,8 +41,8 @@ + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" ) # https://github.com/aws/amazon-cognito-identity-js/blob/master/src/AuthenticationHelper.js#L49 -g_hex = "2" -info_bits = bytearray("Caldera Derived Key", "utf-8") +G_HEX = "2" +INFO_BITS = bytearray("Caldera Derived Key", "utf-8") class HiveAuth: @@ -74,9 +74,9 @@ def __init__( self, username: str, password: str, - deviceGroupKey: str = None, - deviceKey: str = None, - devicePassword: str = None, + device_group_key: str = None, + device_key: str = None, + device_password: str = None, pool_region: str = None, client_secret: str = None, ): @@ -99,25 +99,31 @@ def __init__( self.username = username self.password = password - self.deviceGroupKey = deviceGroupKey - self.deviceKey = deviceKey - self.devicePassword = devicePassword - self.accessToken = None + self.device_group_key = device_group_key + self.device_key = device_key + self.device_password = device_password + self.access_token = None self.user_id = "user_id" self.client_secret = client_secret - self.big_n = hex_to_long(n_hex) - self.g = hex_to_long(g_hex) - self.k = hex_to_long(hex_hash("00" + n_hex + "0" + g_hex)) + self.big_n = hex_to_long(N_HEX) + self.g_value = hex_to_long(G_HEX) + self.k = hex_to_long(hex_hash("00" + N_HEX + "0" + G_HEX)) self.small_a_value = self.generate_random_small_a() self.large_a_value = self.calculate_a() - self.useFile = True if self.username == "use@file.com" else False + self.use_file = bool(self.username == "use@file.com") self.file_response = {"AuthenticationResult": {"AccessToken": "file"}} self.api = HiveApi() self.data = self.api.getLoginInfo() self.__pool_id = self.data.get("UPID") self.__client_id = self.data.get("CLIID") self.__region = self.data.get("REGION").split("_")[0] - self.client = boto3.client("cognito-idp", self.__region) + self.client = boto3.client( + "cognito-idp", + self.__region, + aws_access_key_id="ACCESS_KEY", + aws_secret_access_key="SECRET_KEY", + aws_session_token="SESSION_TOKEN", + ) def generate_random_small_a(self): """Helper function to generate a random big integer. @@ -137,7 +143,7 @@ def calculate_a(self): Returns: int: {Long integer} Computed large A. """ - big_a = pow(self.g, self.small_a_value, self.big_n) + big_a = pow(self.g_value, self.small_a_value, self.big_n) # safety check if (big_a % self.big_n) == 0: raise ValueError("Safety check for A failed") @@ -167,7 +173,7 @@ def get_password_authentication_key( username_password_hash = hash_sha256(username_password.encode("utf-8")) x_value = hex_to_long(hex_hash(pad_hex(salt) + username_password_hash)) - g_mod_pow_xn = pow(self.g, x_value, self.big_n) + g_mod_pow_xn = pow(self.g_value, x_value, self.big_n) int_value2 = server_b_value - self.k * g_mod_pow_xn s_value = pow(int_value2, self.small_a_value + u_value * x_value, self.big_n) hkdf = compute_hkdf( @@ -201,7 +207,7 @@ def get_secret_hash(username, client_id: str, client_secret: str): def generate_hash_device(self, device_group_key, device_key): """Generate the device hash.""" - # source: https://github.com/amazon-archives/amazon-cognito-identity-js/blob/6b87f1a30a998072b4d98facb49dcaf8780d15b0/src/AuthenticationHelper.js#L137 + # source: https://github.com/amazon-archives/amazon-cognito-identity-js/blob/6b87f1a30a998072b4d98facb49dcaf8780d15b0/src/AuthenticationHelper.js#L137 # pylint: disable=line-too-long # random device password, which will be used for DEVICE_SRP_AUTH flow device_password = base64.standard_b64encode(os.urandom(40)).decode("utf-8") @@ -211,9 +217,9 @@ def generate_hash_device(self, device_group_key, device_key): salt = pad_hex(get_random(16)) x_value = hex_to_long(hex_hash(salt + combined_string_hash)) - g = hex_to_long(g_hex) - big_n = hex_to_long(n_hex) - verifier_device_not_padded = pow(g, x_value, big_n) + g_value = hex_to_long(G_HEX) + big_n = hex_to_long(N_HEX) + verifier_device_not_padded = pow(g_value, x_value, big_n) verifier = pad_hex(verifier_device_not_padded) device_secret_verifier_config = { @@ -235,7 +241,7 @@ def get_device_authentication_key( username_password_hash = hash_sha256(username_password.encode("utf-8")) x_value = hex_to_long(hex_hash(pad_hex(salt) + username_password_hash)) - g_mod_pow_xn = pow(self.g, x_value, self.big_n) + g_mod_pow_xn = pow(self.g_value, x_value, self.big_n) int_value2 = server_b_value - self.k * g_mod_pow_xn s_value = pow(int_value2, self.small_a_value + u_value * x_value, self.big_n) hkdf = compute_hkdf( @@ -257,16 +263,16 @@ def process_device_challenge(self, challenge_parameters): datetime.datetime.utcnow().strftime("%a %b %d %H:%M:%S UTC %Y"), ) hkdf = self.get_device_authentication_key( - self.deviceGroupKey, - self.deviceKey, - self.devicePassword, + self.device_group_key, + self.device_key, + self.device_password, hex_to_long(srp_b_hex), salt_hex, ) secret_block_bytes = base64.standard_b64decode(secret_block_b64) msg = ( - bytearray(self.deviceGroupKey, "utf-8") - + bytearray(self.deviceKey, "utf-8") + bytearray(self.device_group_key, "utf-8") + + bytearray(self.device_key, "utf-8") + bytearray(secret_block_bytes) + bytearray(timestamp, "utf-8") ) @@ -277,7 +283,7 @@ def process_device_challenge(self, challenge_parameters): "USERNAME": username, "PASSWORD_CLAIM_SECRET_BLOCK": secret_block_b64, "PASSWORD_CLAIM_SIGNATURE": signature_string.decode("utf-8"), - "DEVICE_KEY": self.deviceKey, + "DEVICE_KEY": self.device_key, } if self.client_secret is not None: response.update( @@ -332,12 +338,12 @@ def process_challenge(self, challenge_parameters: dict): def login(self): """Login into a Hive account.""" - if self.useFile: + if self.use_file: return self.file_response auth_params = self.get_auth_params() - if self.deviceKey is not None: - auth_params["DEVICE_KEY"] = self.deviceKey + if self.device_key is not None: + auth_params["DEVICE_KEY"] = self.device_key response = None result = None try: @@ -348,15 +354,15 @@ def login(self): ) except botocore.exceptions.ClientError as err: if err.__class__.__name__ == "UserNotFoundException": - raise HiveInvalidUsername + raise HiveInvalidUsername from err except botocore.exceptions.EndpointConnectionError as err: if err.__class__.__name__ == "EndpointConnectionError": - raise HiveApiError + raise HiveApiError from err if response["ChallengeName"] == self.PASSWORD_VERIFIER_CHALLENGE: challenge_response = self.process_challenge(response["ChallengeParameters"]) - if self.deviceKey is not None: - challenge_response["DEVICE_KEY"] = self.deviceKey + if self.device_key is not None: + challenge_response["DEVICE_KEY"] = self.device_key try: result = self.client.respond_to_auth_challenge( @@ -366,54 +372,49 @@ def login(self): ) except botocore.exceptions.ClientError as err: if err.__class__.__name__ == "NotAuthorizedException": - raise HiveInvalidPassword + raise HiveInvalidPassword from err if err.__class__.__name__ == "ResourceNotFoundException": - raise HiveInvalidDeviceAuthentication + raise HiveInvalidDeviceAuthentication from err except botocore.exceptions.EndpointConnectionError as err: if err.__class__.__name__ == "EndpointConnectionError": - raise HiveApiError + raise HiveApiError from err return result - else: - raise NotImplementedError( - "The %s challenge is not supported" % response["ChallengeName"] - ) + raise NotImplementedError( + "The %s challenge is not supported" % response["ChallengeName"] + ) - def deviceLogin(self): + def device_login(self): """Perform device login instead.""" - loginResult = self.login() + login_result = self.login() auth_params = self.get_auth_params() - auth_params["DEVICE_KEY"] = self.deviceKey + auth_params["DEVICE_KEY"] = self.device_key - if loginResult.get("ChallengeName") == self.DEVICE_VERIFIER_CHALLENGE: + if login_result.get("ChallengeName") == self.DEVICE_VERIFIER_CHALLENGE: try: - initialResult = self.client.respond_to_auth_challenge( + initial_result = self.client.respond_to_auth_challenge( ClientId=self.__client_id, ChallengeName=self.DEVICE_VERIFIER_CHALLENGE, ChallengeResponses=auth_params, ) - cr = self.process_device_challenge(initialResult["ChallengeParameters"]) + device_challenge_response = self.process_device_challenge( + initial_result["ChallengeParameters"] + ) result = self.client.respond_to_auth_challenge( ClientId=self.__client_id, ChallengeName="DEVICE_PASSWORD_VERIFIER", - ChallengeResponses=cr, + ChallengeResponses=device_challenge_response, ) except botocore.exceptions.EndpointConnectionError as err: if err.__class__.__name__ == "EndpointConnectionError": - raise HiveApiError + raise HiveApiError from err else: raise HiveInvalidDeviceAuthentication return result - def sms_2fa( - self, - entered_code: str, - challenge_parameters: dict, - deviceName=None, - autoDeviceRegistration=True, - ): + def sms_2fa(self, entered_code: str, challenge_parameters: dict): """Process 2FA sms verification.""" session = challenge_parameters.get("Session") code = str(entered_code) @@ -429,93 +430,87 @@ def sms_2fa( }, ) if "NewDeviceMetadata" in result["AuthenticationResult"]: - self.accessToken = result["AuthenticationResult"]["AccessToken"] - self.deviceGroupKey = result["AuthenticationResult"][ + self.access_token = result["AuthenticationResult"]["AccessToken"] + self.device_group_key = result["AuthenticationResult"][ "NewDeviceMetadata" ]["DeviceGroupKey"] - self.deviceKey = result["AuthenticationResult"]["NewDeviceMetadata"][ + self.device_key = result["AuthenticationResult"]["NewDeviceMetadata"][ "DeviceKey" ] - if autoDeviceRegistration: - self.confirmDevice( - self.accessToken, self.deviceKey, self.deviceGroupKey, deviceName - ) - self.updateDeviceStatus(self.accessToken) except botocore.exceptions.ClientError as err: - if ( - err.__class__.__name__ == "NotAuthorizedException" - or err.__class__.__name__ == "CodeMismatchException" + if err.__class__.__name__ in ( + "NotAuthorizedException", + "CodeMismatchException", ): - raise HiveInvalid2FACode + raise HiveInvalid2FACode from err except botocore.exceptions.EndpointConnectionError as err: if err.__class__.__name__ == "EndpointConnectionError": - raise HiveApiError + raise HiveApiError from err return result - def confirmDevice( + def device_registration(self, device_name: str = None): + """Register Device.""" + self.confirm_device(device_name) + self.update_device_status() + + def confirm_device( self, - accessToken: str, - deviceKey: str, - deviceGroupKey: str, - deviceName: str = None, + device_name: str = None, ): """Confirm Device Hive.""" result = None - if deviceName is None: - deviceName = socket.gethostname() + if device_name is None: + device_name = socket.gethostname() try: device_password, device_secret_verifier_config = self.generate_hash_device( - deviceGroupKey, deviceKey + self.device_group_key, self.device_key ) - self.devicePassword = device_password + self.device_password = device_password result = ( self.client.confirm_device( - AccessToken=accessToken, - DeviceKey=deviceKey, - DeviceName=deviceName, + AccessToken=self.access_token, + DeviceKey=self.device_key, + DeviceName=device_name, DeviceSecretVerifierConfig=device_secret_verifier_config, ), ) except botocore.exceptions.EndpointConnectionError as err: if err.__class__.__name__ == "EndpointConnectionError": - raise HiveApiError + raise HiveApiError from err return result - def updateDeviceStatus( - self, - accessToken: str, - ): + def update_device_status(self): """Update Device Hive.""" result = None try: result = ( self.client.update_device_status( - AccessToken=accessToken, - DeviceKey=self.deviceKey, + AccessToken=self.access_token, + DeviceKey=self.device_key, DeviceRememberedStatus="remembered", ), ) except botocore.exceptions.EndpointConnectionError as err: if err.__class__.__name__ == "EndpointConnectionError": - raise HiveApiError + raise HiveApiError from err return result - def getDeviceData(self): + def get_device_data(self): """Get key device information to use device authentication.""" - return self.deviceGroupKey, self.deviceKey, self.devicePassword + return self.device_group_key, self.device_key, self.device_password - def refreshToken( + def refresh_token( self, - refresh_token: str, + token: str, ): """Refresh Hive Tokens.""" result = None - auth_params = ({"REFRESH_TOKEN": refresh_token},) - if self.deviceKey is not None: - auth_params = {"REFRESH_TOKEN": refresh_token, "DEVICE_KEY": self.deviceKey} + auth_params = ({"REFRESH_TOKEN": token},) + if self.device_key is not None: + auth_params = {"REFRESH_TOKEN": token, "DEVICE_KEY": self.device_key} try: result = self.client.initiate_auth( ClientId=self.__client_id, @@ -524,7 +519,7 @@ def refreshToken( ) except botocore.exceptions.EndpointConnectionError as err: if err.__class__.__name__ == "EndpointConnectionError": - raise HiveApiError + raise HiveApiError from err return result @@ -542,8 +537,8 @@ def get_random(nbytes): def hash_sha256(buf): """Authentication Helper hash.""" - a = hashlib.sha256(buf).hexdigest() - return (64 - len(a)) * "0" + a + a_value = hashlib.sha256(buf).hexdigest() + return (64 - len(a_value)) * "0" + a_value def hex_hash(hex_string): @@ -596,6 +591,6 @@ def compute_hkdf(ikm, salt): @private """ prk = hmac.new(salt, ikm, hashlib.sha256).digest() - info_bits_update = info_bits + bytearray(chr(1), "utf-8") + info_bits_update = INFO_BITS + bytearray(chr(1), "utf-8") hmac_hash = hmac.new(prk, info_bits_update, hashlib.sha256).digest() return hmac_hash[:16] diff --git a/pyhiveapi/apyhiveapi/api/hive_auth_async.py b/pyhiveapi/apyhiveapi/api/hive_auth_async.py index 7fa3c17..a30d53b 100644 --- a/pyhiveapi/apyhiveapi/api/hive_auth_async.py +++ b/pyhiveapi/apyhiveapi/api/hive_auth_async.py @@ -1,5 +1,5 @@ """Auth file for logging in.""" - +# pylint: skip-file import asyncio import base64 import binascii diff --git a/pyhiveapi/apyhiveapi/camera.py b/pyhiveapi/apyhiveapi/camera.py index a728d60..7ccd006 100644 --- a/pyhiveapi/apyhiveapi/camera.py +++ b/pyhiveapi/apyhiveapi/camera.py @@ -1,4 +1,5 @@ """Hive Camera Module.""" +# pylint: skip-file class HiveCamera: diff --git a/pyhiveapi/apyhiveapi/device_attributes.py b/pyhiveapi/apyhiveapi/device_attributes.py index 9d94a99..7821181 100644 --- a/pyhiveapi/apyhiveapi/device_attributes.py +++ b/pyhiveapi/apyhiveapi/device_attributes.py @@ -1,4 +1,5 @@ """Hive Device Attribute Module.""" +# pylint: skip-file from .helper.const import HIVETOHA from .helper.logger import Logger diff --git a/pyhiveapi/apyhiveapi/heating.py b/pyhiveapi/apyhiveapi/heating.py index 95906af..3581b9c 100644 --- a/pyhiveapi/apyhiveapi/heating.py +++ b/pyhiveapi/apyhiveapi/heating.py @@ -1,5 +1,5 @@ """Hive Heating Module.""" - +# pylint: skip-file from .helper.const import HIVETOHA diff --git a/pyhiveapi/apyhiveapi/helper/__init__.py b/pyhiveapi/apyhiveapi/helper/__init__.py index b133299..f86f509 100644 --- a/pyhiveapi/apyhiveapi/helper/__init__.py +++ b/pyhiveapi/apyhiveapi/helper/__init__.py @@ -1,3 +1,4 @@ """__init__.py file.""" +# pylint: skip-file from .hive_helper import HiveHelper # noqa: F401 from .logger import Logger # noqa: F401 diff --git a/pyhiveapi/apyhiveapi/helper/const.py b/pyhiveapi/apyhiveapi/helper/const.py index c8dcf88..3b44924 100644 --- a/pyhiveapi/apyhiveapi/helper/const.py +++ b/pyhiveapi/apyhiveapi/helper/const.py @@ -1,4 +1,5 @@ """Constants for Pyhiveapi.""" +# pylint: skip-file SYNC_PACKAGE_NAME = "pyhiveapi" SYNC_PACKAGE_DIR = "/pyhiveapi/" ASYNC_PACKAGE_NAME = "apyhiveapi" diff --git a/pyhiveapi/apyhiveapi/helper/debugger.py b/pyhiveapi/apyhiveapi/helper/debugger.py index 1df982b..526d977 100644 --- a/pyhiveapi/apyhiveapi/helper/debugger.py +++ b/pyhiveapi/apyhiveapi/helper/debugger.py @@ -1,4 +1,5 @@ """Debugger file.""" +# pylint: skip-file import logging diff --git a/pyhiveapi/apyhiveapi/helper/hive_exceptions.py b/pyhiveapi/apyhiveapi/helper/hive_exceptions.py index 7c04e4e..67bd0ae 100644 --- a/pyhiveapi/apyhiveapi/helper/hive_exceptions.py +++ b/pyhiveapi/apyhiveapi/helper/hive_exceptions.py @@ -1,4 +1,5 @@ """Hive exception class.""" +# pylint: skip-file class FileInUse(Exception): diff --git a/pyhiveapi/apyhiveapi/helper/hive_helper.py b/pyhiveapi/apyhiveapi/helper/hive_helper.py index 7083262..2da30d0 100644 --- a/pyhiveapi/apyhiveapi/helper/hive_helper.py +++ b/pyhiveapi/apyhiveapi/helper/hive_helper.py @@ -1,4 +1,5 @@ """Helper class for pyhiveapi.""" +# pylint: skip-file import datetime import operator diff --git a/pyhiveapi/apyhiveapi/helper/hivedataclasses.py b/pyhiveapi/apyhiveapi/helper/hivedataclasses.py index bbe9937..ca9c8fd 100644 --- a/pyhiveapi/apyhiveapi/helper/hivedataclasses.py +++ b/pyhiveapi/apyhiveapi/helper/hivedataclasses.py @@ -1,4 +1,5 @@ """Device data class.""" +# pylint: skip-file from dataclasses import dataclass diff --git a/pyhiveapi/apyhiveapi/helper/logger.py b/pyhiveapi/apyhiveapi/helper/logger.py index 4d91828..db8fe21 100644 --- a/pyhiveapi/apyhiveapi/helper/logger.py +++ b/pyhiveapi/apyhiveapi/helper/logger.py @@ -1,5 +1,5 @@ """Custom Logging Module.""" - +# pylint: skip-file import inspect from datetime import datetime diff --git a/pyhiveapi/apyhiveapi/helper/map.py b/pyhiveapi/apyhiveapi/helper/map.py index 8014b10..afcadd6 100644 --- a/pyhiveapi/apyhiveapi/helper/map.py +++ b/pyhiveapi/apyhiveapi/helper/map.py @@ -1,4 +1,5 @@ """Dot notation for dictionary.""" +# pylint: skip-file class Map(dict): diff --git a/pyhiveapi/apyhiveapi/hive.py b/pyhiveapi/apyhiveapi/hive.py index 902547f..6e0867f 100644 --- a/pyhiveapi/apyhiveapi/hive.py +++ b/pyhiveapi/apyhiveapi/hive.py @@ -1,4 +1,5 @@ """Start Hive Session.""" +# pylint: skip-file import sys import traceback from os.path import expanduser diff --git a/pyhiveapi/apyhiveapi/hotwater.py b/pyhiveapi/apyhiveapi/hotwater.py index e968bbc..08a9e6f 100644 --- a/pyhiveapi/apyhiveapi/hotwater.py +++ b/pyhiveapi/apyhiveapi/hotwater.py @@ -1,4 +1,5 @@ """Hive Hotwater Module.""" +# pylint: skip-file from .helper.const import HIVETOHA diff --git a/pyhiveapi/apyhiveapi/hub.py b/pyhiveapi/apyhiveapi/hub.py index 3cc6c9f..ec3bd92 100644 --- a/pyhiveapi/apyhiveapi/hub.py +++ b/pyhiveapi/apyhiveapi/hub.py @@ -1,5 +1,5 @@ """Hive Hub Module.""" - +# pylint: skip-file from .helper.const import HIVETOHA diff --git a/pyhiveapi/apyhiveapi/light.py b/pyhiveapi/apyhiveapi/light.py index 34f6485..703abe7 100644 --- a/pyhiveapi/apyhiveapi/light.py +++ b/pyhiveapi/apyhiveapi/light.py @@ -1,4 +1,5 @@ """Hive Light Module.""" +# pylint: skip-file import colorsys from .helper.const import HIVETOHA diff --git a/pyhiveapi/apyhiveapi/plug.py b/pyhiveapi/apyhiveapi/plug.py index 4649782..c86e535 100644 --- a/pyhiveapi/apyhiveapi/plug.py +++ b/pyhiveapi/apyhiveapi/plug.py @@ -1,5 +1,5 @@ """Hive Switch Module.""" - +# pylint: skip-file from .helper.const import HIVETOHA diff --git a/pyhiveapi/apyhiveapi/sensor.py b/pyhiveapi/apyhiveapi/sensor.py index 46e51c5..ceb0b99 100644 --- a/pyhiveapi/apyhiveapi/sensor.py +++ b/pyhiveapi/apyhiveapi/sensor.py @@ -1,5 +1,5 @@ """Hive Sensor Module.""" - +# pylint: skip-file from .helper.const import HIVE_TYPES, HIVETOHA, sensor_commands diff --git a/pyhiveapi/apyhiveapi/session.py b/pyhiveapi/apyhiveapi/session.py index 41c0404..06c537e 100644 --- a/pyhiveapi/apyhiveapi/session.py +++ b/pyhiveapi/apyhiveapi/session.py @@ -1,4 +1,5 @@ """Hive Session Module.""" +# pylint: skip-file import asyncio import copy import json diff --git a/setup.py b/setup.py index 1ef0a50..dbdda79 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ """Setup pyhiveapi package.""" +# pylint: skip-file import os import re @@ -15,7 +16,7 @@ def requirements_from_file(filename="requirements.txt"): setup( - version="0.5.10", + version="0.5.11", package_data={"data": ["*.json"]}, include_package_data=True, cmdclass={