diff --git a/CHANGELOG.md b/CHANGELOG.md index 86fcada..2496dd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ # LoginRadius Python SDK Change Log + + +# Version 11.3.0 + +## Enhancements + +- Added a feature to add ApiKey and ApiSecret directly in LoginRadius manual SOTT generation method. +- Code optimization for better performance. +- Added Licence and Contribution Guideline files. + +## Breaking Changes + +For developers migrating from v11.2.0, there will be 1 minor breaking change in terms of SDK implementation. In this version, we have added a feature to add ApiKey & ApiSecret directly into the manual SOTT generation method `get_sott()`. + # Version 11.2.0 ## Enhancements diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..76aab7c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,41 @@ +# Contributing + +[PYTHON SDK](https://github.com/LoginRadius/python-sdk) is [MIT](LICENSE) licensed and accepts contributions via GitHub pull requests. This document outlines some of the conventions on development workflow, commit message formatting, contact points, and other resources to make it easier to get your contribution accepted. + +## Getting Started + +- Fork the repository on GitHub. +- If you find any bug or Improvement in our existing code-base, please create a pull request as mentioned in Contribution Flow. + +## Contribution Flow + +This is a rough outline of what a contributor's workflow looks like: + +- Create a separate branch from the `dev` branch to base your work. +- Make commits of logical units. +- Make sure your commit messages are in the proper format (see below). +- Push your changes to a topic branch in your fork of the repository. +- Submit a pull request to the original repository. +- **Please ensure that you raise a PR on the `dev` branch instead of `master`.** + +#### Commit Messages + +Please follow the below format while writing commit messages: + +``` + title: One line description about your change + + description: An optional description of your changes. +``` + +Thanks for your contributions! + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +### Our Responsibilities + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..c3ac770 --- /dev/null +++ b/LICENCE @@ -0,0 +1,19 @@ +Copyright (c) 2022 LoginRadius Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 47ea4c7..5e12983 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,13 @@ You will need at least Python - 2.7 or greater. LoginRadius module utilizes the Using pip ``` - pip install loginradius-v2==11.2.0 + pip install loginradius-v2==11.3.0 ``` or with easy_install ``` - easy_install loginradius-v2==11.2.0 + easy_install loginradius-v2==11.3.0 ``` ### Install From Source @@ -4250,6 +4250,29 @@ else: ```
+ +### Generate SOTT Manually + +SOTT is a secure one-time token that can be created using the API key, API secret, and a timestamp ( start time and end time ). You can manually create a SOTT using the following utility function. + + +``` +timeDifference='10' #(Optional) The time difference will be used to set the expiration time of SOTT, If you do not pass time difference then the default expiration time of SOTT is 10 minutes. + +getLRserverTime=False #(Optional) If True it will call LoginRadius Get Server Time Api and fetch basic server information and server time information which is useful when generating an SOTT token. + +# The LoginRadius API key and primary API secret can be passed additionally, If the credentials will not be passed then this SOTT function will pick the API credentials from the SDK configuration. + + +apiKey="" #(Optional) LoginRadius Api Key. + +apiSecret = "" # (Optional) LoginRadius Api Secret (Only Primary Api Secret is used to generate the SOTT manually). + +sott_data = loginradius.get_sott(timeDifference, getLRserverTime,apiKey,apiSecret) + +print(sott_data) +``` + ## Demo We have a demo web application using the Python SDK, which includes the following features: diff --git a/demo/LoginRadius/__init__.py b/demo/LoginRadius/__init__.py index b932083..4780a79 100644 --- a/demo/LoginRadius/__init__.py +++ b/demo/LoginRadius/__init__.py @@ -20,7 +20,7 @@ __copyright__ = "Copyright 2019, LoginRadius" __email__ = "developers@loginradius.com" __status__ = "Production" -__version__ = "11.2.0" +__version__ = "11.3.0" import json import sys @@ -219,7 +219,8 @@ def get_digest(self, expiry_time, url, payload=None): if sys.version_info[0] >= 3: key_bytes = bytes(self.get_api_secret(), 'latin-1') data_bytes = bytes(signing_str, 'latin-1') - dig = hmac.new(key_bytes, msg=data_bytes, digestmod=hashlib.sha256).digest() + dig = hmac.new(key_bytes, msg=data_bytes, + digestmod=hashlib.sha256).digest() if sys.version_info[0] >= 3: return base64.b64encode(dig).decode("utf-8") return base64.b64encode(dig) @@ -243,7 +244,8 @@ def execute(self, method, resource_url, query_params, payload): 'Accept-encoding': 'gzip'} if "access_token" in query_params and "/auth" in resource_url: - headers.update({"Authorization": "Bearer " + query_params['access_token']}) + headers.update({"Authorization": "Bearer " + + query_params['access_token']}) query_params.pop("access_token") if "sott" in query_params: @@ -306,7 +308,7 @@ def _get_json(self, url, payload, HEADERS): http = urllib3.ProxyManager(proxies['https']) else: http = urllib3.PoolManager() - + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) r = http.request('GET', url, fields=payload, headers=HEADERS) if(r.status == 429): @@ -354,7 +356,8 @@ def get_api_secret(self): def _get_proxy(self): if self.IS_PROXY_ENABLE: - proxies = {'https': 'https://' + self.USER_NAME + ':' + self.PASSWORD + '@' + self.HOST + ':' + self.PORT} + proxies = {'https': 'https://' + self.USER_NAME + ':' + + self.PASSWORD + '@' + self.HOST + ':' + self.PORT} else: proxies = {} return proxies @@ -387,10 +390,32 @@ def is_null_or_whitespace(self, value): def get_validation_message(self, field): return "Invalid value for field " + str(field) - def get_sott(self, time='10', getLRserverTime=False): - if getLRserverTime: - result = self.configuration.get_server_info() + # + # Function to generate SOTT manually + # + def get_sott(self, timeDifference='', getLRserverTime=False, apiKey="", apiSecret=""): + + time = '10' + secret = self.API_SECRET + key = self.API_KEY + + if(not self.is_null_or_whitespace(timeDifference)): + time = timeDifference + + if(not self.is_null_or_whitespace(apiSecret)): + secret = apiSecret + if(not self.is_null_or_whitespace(apiKey)): + key = apiKey + + now = datetime.utcnow() + now = now - timedelta(minutes=0) + now_plus_10m = now + timedelta(minutes=int(time)) + now = now.strftime("%Y/%m/%d %I:%M:%S") + now_plus_10m = now_plus_10m.strftime("%Y/%m/%d %I:%M:%S") + + if getLRserverTime: + result = self.configuration.get_server_info(time) if result.get('Sott') is not None: Sott = result.get('Sott') for timeKey, val in Sott.items(): @@ -398,21 +423,8 @@ def get_sott(self, time='10', getLRserverTime=False): now = val if timeKey == 'EndTime': now_plus_10m = val - else: - now = datetime.utcnow() - now = now - timedelta(minutes=5) - now_plus_10m = now + timedelta(minutes=10) - now = now.strftime("%Y/%m/%d %I:%M:%S") - now_plus_10m = now_plus_10m.strftime("%Y/%m/%d %I:%M:%S") - - else: - now = datetime.utcnow() - now = now - timedelta(minutes=5) - now_plus_10m = now + timedelta(minutes=10) - now = now.strftime("%Y/%m/%d %I:%M:%S") - now_plus_10m = now_plus_10m.strftime("%Y/%m/%d %I:%M:%S") - plaintext = now + "#" + self.API_KEY + "#" + now_plus_10m + plaintext = now + "#" + key + "#" + now_plus_10m padding = 16 - (len(plaintext) % 16) if sys.version_info[0] == 3: plaintext += (bytes([padding]) * padding).decode() @@ -420,7 +432,7 @@ def get_sott(self, time='10', getLRserverTime=False): plaintext += (chr(padding) * padding).decode() salt = "\0\0\0\0\0\0\0\0" - cipher_key = PBKDF2(self.API_SECRET, + cipher_key = PBKDF2(secret, salt, 10000).read(self.CONST_KEYSIZE // 8) if sys.version_info[0] == 3: diff --git a/lib/LoginRadius/__init__.py b/lib/LoginRadius/__init__.py index b932083..d7db476 100644 --- a/lib/LoginRadius/__init__.py +++ b/lib/LoginRadius/__init__.py @@ -20,7 +20,7 @@ __copyright__ = "Copyright 2019, LoginRadius" __email__ = "developers@loginradius.com" __status__ = "Production" -__version__ = "11.2.0" +__version__ = "11.3.0" import json import sys @@ -387,10 +387,32 @@ def is_null_or_whitespace(self, value): def get_validation_message(self, field): return "Invalid value for field " + str(field) - def get_sott(self, time='10', getLRserverTime=False): - if getLRserverTime: - result = self.configuration.get_server_info() + # + # Function to generate SOTT manually + # + def get_sott(self, timeDifference='', getLRserverTime=False, apiKey="", apiSecret=""): + + time = '10' + secret = self.API_SECRET + key = self.API_KEY + + if(not self.is_null_or_whitespace(timeDifference)): + time = timeDifference + + if(not self.is_null_or_whitespace(apiSecret)): + secret = apiSecret + + if(not self.is_null_or_whitespace(apiKey)): + key = apiKey + now = datetime.utcnow() + now = now - timedelta(minutes=0) + now_plus_10m = now + timedelta(minutes=int(time)) + now = now.strftime("%Y/%m/%d %I:%M:%S") + now_plus_10m = now_plus_10m.strftime("%Y/%m/%d %I:%M:%S") + + if getLRserverTime: + result = self.configuration.get_server_info(time) if result.get('Sott') is not None: Sott = result.get('Sott') for timeKey, val in Sott.items(): @@ -398,21 +420,8 @@ def get_sott(self, time='10', getLRserverTime=False): now = val if timeKey == 'EndTime': now_plus_10m = val - else: - now = datetime.utcnow() - now = now - timedelta(minutes=5) - now_plus_10m = now + timedelta(minutes=10) - now = now.strftime("%Y/%m/%d %I:%M:%S") - now_plus_10m = now_plus_10m.strftime("%Y/%m/%d %I:%M:%S") - - else: - now = datetime.utcnow() - now = now - timedelta(minutes=5) - now_plus_10m = now + timedelta(minutes=10) - now = now.strftime("%Y/%m/%d %I:%M:%S") - now_plus_10m = now_plus_10m.strftime("%Y/%m/%d %I:%M:%S") - plaintext = now + "#" + self.API_KEY + "#" + now_plus_10m + plaintext = now + "#" + key + "#" + now_plus_10m padding = 16 - (len(plaintext) % 16) if sys.version_info[0] == 3: plaintext += (bytes([padding]) * padding).decode() @@ -420,7 +429,7 @@ def get_sott(self, time='10', getLRserverTime=False): plaintext += (chr(padding) * padding).decode() salt = "\0\0\0\0\0\0\0\0" - cipher_key = PBKDF2(self.API_SECRET, + cipher_key = PBKDF2(secret, salt, 10000).read(self.CONST_KEYSIZE // 8) if sys.version_info[0] == 3: diff --git a/lib/README.md b/lib/README.md index 08282a5..de014e3 100644 --- a/lib/README.md +++ b/lib/README.md @@ -21,18 +21,30 @@ From Package Using pip ``` - pip install loginradius-v2==11.2.0 + pip install loginradius-v2==11.3.0 ``` or with easy_install ``` - easy_install loginradius-v2==11.2.0 + easy_install loginradius-v2==11.3.0 ``` Changelog ====== +11.3.0 +----------- +Release on **January 31, 2022** +## Enhancements + +- Added a feature to add ApiKey and ApiSecret directly in LoginRadius manual SOTT generation method. +- Code optimization for better performance. +- Added Licence and Contribution Guideline files. + +## Breaking Changes + +For developers migrating from v11.2.0, there will be 1 minor breaking change in terms of SDK implementation. In this version, we have added a feature to add ApiKey & ApiSecret directly into the manual SOTT generation method `get_sott()`. 11.2.0 ----------- diff --git a/lib/setup.py b/lib/setup.py index d9709ce..e20d5cc 100644 --- a/lib/setup.py +++ b/lib/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name = 'LoginRadius-v2', - version='11.2.0', + version='11.3.0', long_description=long_description, long_description_content_type='text/markdown', packages=setuptools.find_packages(),