Support multiple authentication headers in requests (#358)
* Implement parse_authentication_tokens * Update documentation * Update documentation
This commit is contained in:
Родитель
dee2a0b124
Коммит
0594dfcc23
|
@ -2,7 +2,7 @@
|
|||
|
||||
RESTler supports token-based authentication.
|
||||
|
||||
The user must provide a separate program to generate tokens, which implements the authentication method required by the API. This will be invoked in a separate process by RESTler to obtain and regularly refresh tokens. When invoked, this program must print metadata about the tokens on the first line, followed by each token and the required token header on a separate line. For example:
|
||||
The user must provide a separate program to generate tokens, which implements the authentication method required by the API. This will be invoked in a separate process by RESTler to obtain and regularly refresh tokens. When invoked, this program must print metadata about the tokens on the first line, followed by each token and the required token header on a separate line for each application. For example:
|
||||
|
||||
`>my_gettoken.exe <args to my_gettoken>`
|
||||
|
||||
|
@ -12,6 +12,16 @@ ApiTokenTag: 9A
|
|||
ApiTokenTag: ZQ
|
||||
```
|
||||
|
||||
When multiple token headers are required for each request, they could be defined on multiple lines, separated with the delimiter `---` on a new line:
|
||||
|
||||
```
|
||||
{u'app1': {}, u'app2':{}}
|
||||
ApiTokenTag: 9A
|
||||
ApiTokenTag2: 9B
|
||||
---
|
||||
ApiTokenTag: ZQ
|
||||
ApiTokenTag2: BZZ
|
||||
```
|
||||
|
||||
RESTler will obtain new tokens by invoking the token generation script with the frequency specified in the *--token_refresh_interval* option.
|
||||
|
||||
|
|
|
@ -70,16 +70,8 @@ def execute_token_refresh_cmd(cmd):
|
|||
cmd_result = subprocess.getoutput(str(cmd).split(' '))
|
||||
else:
|
||||
cmd_result = subprocess.getoutput([cmd])
|
||||
metadata = cmd_result.split("\n")[0]
|
||||
if not metadata:
|
||||
raise EmptyTokenException
|
||||
metadata = ast.literal_eval(metadata)
|
||||
latest_token_value = cmd_result.split("\n")[1].strip('\r') + "\r\n"
|
||||
if not latest_token_value:
|
||||
raise EmptyTokenException
|
||||
n_apps = len(metadata.keys())
|
||||
if n_apps == 2:
|
||||
latest_shadow_token_value = cmd_result.split("\n")[2].strip('\r') + "\r\n"
|
||||
|
||||
_, latest_token_value, latest_shadow_token_value = parse_authentication_tokens(cmd_result)
|
||||
_RAW_LOGGING(f"New value: {cmd_result}")
|
||||
break
|
||||
except subprocess.CalledProcessError:
|
||||
|
@ -104,6 +96,56 @@ def execute_token_refresh_cmd(cmd):
|
|||
_RAW_LOGGING(f"\nMaximum number of retries ({MAX_RETRIES}) exceeded. Exiting program.")
|
||||
sys.exit(-1)
|
||||
|
||||
def parse_authentication_tokens(cmd_result):
|
||||
""" Parses the output @param cmd_result from token scripts to refresh tokens.
|
||||
|
||||
Format:
|
||||
{u'app1': {}, u'app2':{}} // Metadata
|
||||
ApiTokenTag: 9A // Auth header for application 1
|
||||
ApiTokenTag: ZQ // Auth header for application 2
|
||||
|
||||
Format for multiple authenication headers per request:
|
||||
{u'app1': {}, u'app2':{}} // Metadata
|
||||
ApiTokenTag: 9A // Auth header for application 1
|
||||
ApiTokenTag2: E8 // Auth header for application 1
|
||||
--- // Delimiter
|
||||
ApiTokenTag: ZQ // Auth header for application 2
|
||||
ApiTokenTag2: UI // Auth header for application 2
|
||||
|
||||
@param cmd_result: The result of the user-provided command to refresh the token.
|
||||
@type cmd_result: Str
|
||||
|
||||
@return: Metadata, token values and shadow token values
|
||||
@rtype : Tuple[Dict, Str, Str]
|
||||
|
||||
"""
|
||||
token_value = NO_TOKEN_SPECIFIED
|
||||
shadow_token_value = NO_SHADOW_TOKEN_SPECIFIED
|
||||
DELIMITER = '---'
|
||||
|
||||
metadata = cmd_result.split("\n")[0]
|
||||
if not metadata:
|
||||
raise EmptyTokenException
|
||||
metadata = ast.literal_eval(metadata)
|
||||
|
||||
n_apps = len(metadata.keys())
|
||||
tokens = [line.strip() for line in cmd_result.strip().split('\n')[1:]]
|
||||
|
||||
if n_apps == 1 and DELIMITER not in tokens:
|
||||
token_value = '\r\n'.join(tokens) + '\r\n'
|
||||
elif n_apps == 2 and DELIMITER not in tokens:
|
||||
token_value = tokens[0] + '\r\n'
|
||||
shadow_token_value = tokens[1] + '\r\n'
|
||||
else:
|
||||
token_value = '\r\n'.join(tokens[:tokens.index(DELIMITER)]) + '\r\n'
|
||||
if n_apps == 2:
|
||||
shadow_token_value = '\r\n'.join(tokens[tokens.index(DELIMITER)+1:]) + '\r\n'
|
||||
|
||||
if not latest_token_value:
|
||||
raise EmptyTokenException
|
||||
|
||||
return metadata, token_value, shadow_token_value
|
||||
|
||||
def replace_auth_token(data, replace_str):
|
||||
""" Replaces any authentication tokens from a data string with a
|
||||
specified @replace_str and returns the new data
|
||||
|
@ -119,9 +161,9 @@ def replace_auth_token(data, replace_str):
|
|||
"""
|
||||
if data:
|
||||
if latest_token_value:
|
||||
data = data.replace(latest_token_value.split('\r\n')[0], replace_str)
|
||||
data = data.replace(latest_token_value.strip('\r\n'), replace_str)
|
||||
if latest_shadow_token_value:
|
||||
data = data.replace(latest_shadow_token_value.split('\r\n')[0], replace_str)
|
||||
data = data.replace(latest_shadow_token_value.strip('\r\n'), replace_str)
|
||||
return data
|
||||
|
||||
def resolve_dynamic_primitives(values, candidate_values_pool):
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import unittest
|
||||
|
||||
from engine.core.request_utilities import parse_authentication_tokens, NO_TOKEN_SPECIFIED, NO_SHADOW_TOKEN_SPECIFIED
|
||||
|
||||
class AuthParserTest(unittest.TestCase):
|
||||
def test_auth_parser(self):
|
||||
global latest_token_value, latest_shadow_token_value
|
||||
latest_token_value = NO_TOKEN_SPECIFIED
|
||||
latest_shadow_token_value = NO_SHADOW_TOKEN_SPECIFIED
|
||||
|
||||
testcases = [
|
||||
("{u'app1': {}}\nApiTokenTag: 9A\n", ('ApiTokenTag: 9A\r\n', NO_SHADOW_TOKEN_SPECIFIED)),
|
||||
("{u'app1': {}}\nApiTokenTag: 9A\nApiTokenTag2: 9B\n", ('ApiTokenTag: 9A\r\nApiTokenTag2: 9B\r\n', NO_SHADOW_TOKEN_SPECIFIED)),
|
||||
("{u'app1': {}}\nApiTokenTag: 9A\nApiTokenTag2: 9B\n---\n", ('ApiTokenTag: 9A\r\nApiTokenTag2: 9B\r\n', NO_SHADOW_TOKEN_SPECIFIED)),
|
||||
|
||||
("{u'app1': {}, u'app2': {}}\nApiTokenTag: 9A\nApiTokenTag: 9B\n", ('ApiTokenTag: 9A\r\n', 'ApiTokenTag: 9B\r\n')),
|
||||
("{u'app1': {}, u'app2': {}}\nApiTokenTag: 9A\n---\nApiTokenTag: 9B\n", ('ApiTokenTag: 9A\r\n', 'ApiTokenTag: 9B\r\n')),
|
||||
("{u'app1': {}, u'app2': {}}\nApiTokenTag: 9A\nApiTokenTag2: 9C\n---\nApiTokenTag: 9B\nApiTokenTag2: 9D\n", ('ApiTokenTag: 9A\r\nApiTokenTag2: 9C\r\n', 'ApiTokenTag: 9B\r\nApiTokenTag2: 9D\r\n')),
|
||||
]
|
||||
for data, expected in testcases:
|
||||
latest_token_value = NO_TOKEN_SPECIFIED
|
||||
latest_shadow_token_value = NO_SHADOW_TOKEN_SPECIFIED
|
||||
_, latest_token_value, latest_shadow_token_value = parse_authentication_tokens(data)
|
||||
self.assertEqual(expected, (latest_token_value, latest_shadow_token_value))
|
Загрузка…
Ссылка в новой задаче