Support multiple authentication headers in requests (#358)

* Implement parse_authentication_tokens

* Update documentation

* Update documentation
This commit is contained in:
Victor Li 2021-10-27 19:54:07 +02:00 коммит произвёл GitHub
Родитель dee2a0b124
Коммит 0594dfcc23
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 89 добавлений и 13 удалений

Просмотреть файл

@ -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))