Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authd replacement configurations QA #2171

Merged
merged 57 commits into from
Nov 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
8a01449
Removed use_only_authd test (#2067)
Kondent Oct 21, 2021
a44ff5e
Improving validate_authd_response method logic
pereyra-m Oct 20, 2021
489b78e
PoC for authd
pereyra-m Oct 4, 2021
87fd505
Remove extra restart
pereyra-m Oct 13, 2021
65033a3
Sending messages to socket
pereyra-m Oct 13, 2021
ed01098
Implement insert_pre_existent_keys logic
Oct 15, 2021
00eb711
Relocate force_options tests
Oct 15, 2021
7cdd951
Improve input format of the test cases
Oct 18, 2021
1f7a791
Improve tests load logic
Oct 18, 2021
9da0ab4
Improve logs check
Oct 19, 2021
325965f
Improve WDB query logic
Oct 20, 2021
7ed3211
Code review changes
Oct 25, 2021
d42d1e9
Rework force_options configuration block
MiguelazoDS Oct 25, 2021
d2d572a
Fix after rebase
MiguelazoDS Oct 26, 2021
f31c7a7
Updating tests with new force configuration block
pereyra-m Oct 19, 2021
e2c4c1e
Adding new test cases
pereyra-m Oct 20, 2021
5b67dd9
Updating fixtures and tests
pereyra-m Oct 20, 2021
bed54ae
Adapting worker tests
pereyra-m Oct 20, 2021
daad785
Updating comments
pereyra-m Oct 20, 2021
0927230
Minor fixes after review
pereyra-m Oct 28, 2021
9f1c1b0
Moving insert_pre_existent_agents fixture
pereyra-m Oct 28, 2021
9a7262b
Fix after rebase
pereyra-m Oct 28, 2021
ac0e442
Updating worker messages
pereyra-m Oct 28, 2021
2e67d6f
Using wazuh_control in check_daemon_status for authd tests
pereyra-m Nov 1, 2021
d0babf2
Replacing old force option by the new force block
MiguelazoDS Oct 20, 2021
9f31ba6
Fix rebase errors
Nov 3, 2021
10b9b1a
Using wazuh_control in check_daemon_status for authd tests
pereyra-m Nov 1, 2021
025a5f2
Implement legacy force options tests
Oct 26, 2021
7900809
Key mismatch test cases
Oct 26, 2021
a5dc81d
Force options enablement test cases
Oct 26, 2021
b1ac5fd
Implement authd disconnected_time test cases
Oct 27, 2021
c954ea0
Implement after_registration_time test cases
Oct 27, 2021
6b432a2
Implement force_options invalid test cases
Oct 28, 2021
6aa02d9
Add IP scenarios to registration time tests
Oct 28, 2021
84150b0
Add duplicated ip scenarios to disconnected time tests
Oct 28, 2021
12ac50f
Add IP scenarios to key_mismatch tests
Oct 28, 2021
b10e26f
Add IP scenarios to enablement tests
Oct 29, 2021
b8aaec9
Fix rebase errors
Oct 29, 2021
e524457
Relocate override config functions
Oct 29, 2021
6b043c5
Relocate authd log validator
Oct 29, 2021
085e161
Fix PEP8
Oct 29, 2021
fb9180e
Code review changes
Nov 2, 2021
158b9c1
Fix Authd import
Nov 2, 2021
f8bb076
Deprecate old force_insert tests
Nov 2, 2021
8025016
Invalid configs with an atomic restart call
Nov 3, 2021
d038eec
Reimplement key_hash test as a parser test
Nov 3, 2021
ca0f773
Fix rebase errors
Nov 3, 2021
a8f5960
Fix Wazuh-DB with disconnection_time
Nov 3, 2021
1101f8d
Fix Authd ssl certs tests
Nov 3, 2021
017a1d1
Fix remote_enrollment tests
Nov 3, 2021
24c222b
Merge pull request #2151 from wazuh/dev-2057-force_options-tests-impl…
DProvinciani Nov 4, 2021
3f9b9a2
Merge pull request #2170 from wazuh/dev-2167-wdb-tests-with-disconnec…
DProvinciani Nov 4, 2021
2964342
Adding new test descriptions and revising already documented tests
MiguelazoDS Nov 4, 2021
56d473b
Recover missing files after rebase
Nov 4, 2021
246694e
Code review changes
MiguelazoDS Nov 4, 2021
80c59c4
Merge pull request #2176 from wazuh/dev-2154-tests_documentation
DProvinciani Nov 4, 2021
4bb3ed3
Fix PEP8
Nov 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions deps/wazuh_testing/wazuh_testing/authd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
'''
brief: This module holds common methods and variables for the authd tests
copyright:
Copyright (C) 2015-2021, Wazuh Inc.
Created by Wazuh, Inc. <[email protected]>.
This program is free software; you can redistribute it and/or modify it under the terms of GPLv2
'''

import re
import pytest
import time
from wazuh_testing.tools import CLIENT_KEYS_PATH, LOG_FILE_PATH
from wazuh_testing.wazuh_db import query_wdb
from wazuh_testing.tools.monitoring import FileMonitor, make_callback, AUTHD_DETECTOR_PREFIX


DAEMON_NAME = 'wazuh-authd'
AUTHD_KEY_REQUEST_TIMEOUT = 10


def create_authd_request(input):
"""
Creates a command to request keys to Authd.

Args:
input (dict): Dictionary with the content of the request command.
"""
command = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
command = ""
command = ''


if 'password' in input:
password = input['password']
command = command + f'OSSEC PASS: {password} '
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
command = command + f'OSSEC PASS: {password} '
command = command + f"OSSEC PASS: {password} "


command = command + 'OSSEC'

if 'name' in input:
name = input['name']
command = command + f" A:'{name}'"
else:
raise Exception("Error creating the Authd command: 'name' is required")

if 'group' in input:
group = input['group']
command = command + f" G:'{group}'"

if 'ip' in input:
ip = input['ip']
command = command + f" IP:'{ip}'"

if 'key_hash' in input:
key_hash = input['key_hash']
command = command + f" K:'{key_hash}'"

return command


# Functions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Functions

def validate_authd_logs(expected_logs, log_monitor=None):
if not log_monitor:
log_monitor = FileMonitor(LOG_FILE_PATH)

for log in expected_logs:
log_monitor.start(timeout=AUTHD_KEY_REQUEST_TIMEOUT,
callback=make_callback(log, prefix=AUTHD_DETECTOR_PREFIX,
escape=True),
error_message=f"Expected log does not occured: '{log}'")


def validate_argument(received, expected, argument_name):
if received != expected:
return 'error', f"Invalid '{argument_name}': '{received}' received, '{expected}' expected."
else:
return 'success', ''


def validate_authd_response(response, expected):
"""
Validates if the different items of an Authd response are as expected. Any item inexistent in expected won't
be validated.

Args:
response (str): The Authd response to be validated.
expected (dict): Dictionary with the items to validate.
"""
response = response.split(sep=" ", maxsplit=1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
response = response.split(sep=" ", maxsplit=1)
response = response.split(sep=' ', maxsplit=1)

status = response[0]
result = 'success'
err_msg = ''
if expected['status'] == 'success':
result, err_msg = validate_argument(status, 'OSSEC', 'status')
if result != 'success':
return result, err_msg

agent_key = response[1].split('\'')[1::2][0].split()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little difficult to read, maybe it should be a good idea to add a comment with an example of a full response in order to understand the reason for this string logic.

id = agent_key[0]
name = agent_key[1]
ip = agent_key[2]
key = agent_key[3]
Comment on lines +95 to +98
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
id = agent_key[0]
name = agent_key[1]
ip = agent_key[2]
key = agent_key[3]
id, name, ip, key = agent_key[0:4]


if 'id' in expected:
result, err_msg = validate_argument(id, expected['id'], 'id')
if result != 'success':
return result, err_msg

if 'name' in expected:
result, err_msg = validate_argument(name, expected['name'], 'name')
if result != 'success':
return result, err_msg

if 'ip' in expected:
result, err_msg = validate_argument(ip, expected['ip'], 'ip')
if result != 'success':
return result, err_msg

if 'key' in expected:
result, err_msg = validate_argument(key, expected['key'], 'key')
if result != 'success':
return result, err_msg

elif expected['status'] == 'error':
result, err_msg = validate_argument(status, 'ERROR:', 'status')
if result != 'success':
return result, err_msg

message = response[1]
if 'message' in expected:
if re.match(expected['message'], message) is None:
return 'error', f"Invalid 'message': '{message}' received, '{expected['message']}' expected"
else:
raise Exception('Invalid expected status')

return result, err_msg
Comment on lines +76 to +132
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is a little hard to read and it has code repetition. In future revisions of this module we could use something like the following:

Suggested change
def validate_authd_response(response, expected):
"""
Validates if the different items of an Authd response are as expected. Any item inexistent in expected won't
be validated.
Args:
response (str): The Authd response to be validated.
expected (dict): Dictionary with the items to validate.
"""
response = response.split(sep=" ", maxsplit=1)
status = response[0]
result = 'success'
err_msg = ''
if expected['status'] == 'success':
result, err_msg = validate_argument(status, 'OSSEC', 'status')
if result != 'success':
return result, err_msg
agent_key = response[1].split('\'')[1::2][0].split()
id = agent_key[0]
name = agent_key[1]
ip = agent_key[2]
key = agent_key[3]
if 'id' in expected:
result, err_msg = validate_argument(id, expected['id'], 'id')
if result != 'success':
return result, err_msg
if 'name' in expected:
result, err_msg = validate_argument(name, expected['name'], 'name')
if result != 'success':
return result, err_msg
if 'ip' in expected:
result, err_msg = validate_argument(ip, expected['ip'], 'ip')
if result != 'success':
return result, err_msg
if 'key' in expected:
result, err_msg = validate_argument(key, expected['key'], 'key')
if result != 'success':
return result, err_msg
elif expected['status'] == 'error':
result, err_msg = validate_argument(status, 'ERROR:', 'status')
if result != 'success':
return result, err_msg
message = response[1]
if 'message' in expected:
if re.match(expected['message'], message) is None:
return 'error', f"Invalid 'message': '{message}' received, '{expected['message']}' expected"
else:
raise Exception('Invalid expected status')
return result, err_msg
def get_authd_response_values(response):
authd_response_dict = {}
try:
response = response.split(sep=' ', maxsplit=1)
status, message = response[0:1]
authd_response_dict['status'] = status
authd_response_dict['message'] = message
if status == 'success':
agent_key = message.split('\'')[1::2][0].split()
id, name, ip, key = agent_key[0:4]
authd_response_dict['id'] = id
authd_response_dict['name'] = name
authd_response_dict['ip'] = ip
authd_response_dict['key'] = key
except:
raise IndexError(f"Authd response {response} does not contains expected values: {str(values)}")
return authd_response_dict
def validate_authd_response(response, expected):
"""
Validates if the different items of an Authd response are as expected. Any item inexistent in expected won't
be validated.
Args:
response (str): The Authd response to be validated.
expected (dict): Dictionary with the items to validate.
"""
authd_response_dict = get_authd_response_values(response)
result = 'success'
err_msg = ''
for expected_key in expected.keys():
if expected_key == 'message':
if re.match(expected[expected_key], authd_response_dict[message]) is None:
return 'error', f"Invalid 'message': '{message}' received, '{expected['message']}' expected"
elif expected_key in ['status', 'id', 'name', 'ip', 'key']:
if expected_key == 'status':
expected_value = 'OSSEC' if expected['status'] == 'success' else 'ERROR'
else:
expected_value = expected[expected_key]
result, err_msg = validate_argument(authd_response_dict[expected_value], expected_value, expected_key)
else:
raise Exception('Invalid expected status')
return result, err_msg



def clean_agents_from_db():
"""
Clean agents from DB
"""
command = 'global sql DELETE FROM agent WHERE id != 0'
try:
query_wdb(command)
except Exception:
raise Exception('Unable to clean agents')
Comment on lines +135 to +143
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe is more intuitive to move this to wazuh_db due this is not properly an authd related function.



def insert_agent_in_db(id=1, name="TestAgent", ip="any", registration_time=0, connection_status=0,
disconnection_time=0):
Comment on lines +146 to +147
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def insert_agent_in_db(id=1, name="TestAgent", ip="any", registration_time=0, connection_status=0,
disconnection_time=0):
def insert_agent_in_db(id=1, name='TestAgent', ip='any', registration_time=0, connection_status=0,
disconnection_time=0):

"""
Write agent in global.db
"""
insert_command = f'global insert-agent {{"id":{id},"name":"{name}","ip":"{ip}","date_add":{registration_time}}}'
update_command = f'global sql UPDATE agent SET connection_status = "{connection_status}",\
disconnection_time = "{disconnection_time}" WHERE id = {id};'
try:
query_wdb(insert_command)
query_wdb(update_command)
except Exception:
raise Exception(f'Unable to add agent {id}')
Comment on lines +146 to +158
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same that before, these functions could be useful for the rest of the modules and they are more related with wazuh_db module.

Comment on lines +146 to +158
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def insert_agent_in_db(id=1, name="TestAgent", ip="any", registration_time=0, connection_status=0,
disconnection_time=0):
"""
Write agent in global.db
"""
insert_command = f'global insert-agent {{"id":{id},"name":"{name}","ip":"{ip}","date_add":{registration_time}}}'
update_command = f'global sql UPDATE agent SET connection_status = "{connection_status}",\
disconnection_time = "{disconnection_time}" WHERE id = {id};'
try:
query_wdb(insert_command)
query_wdb(update_command)
except Exception:
raise Exception(f'Unable to add agent {id}')
def insert_agent_in_db(id=1, name="TestAgent", ip="any", registration_time=0, connection_status=0,
disconnection_time=0):
"""
Write agent in global.db
"""
insert_command = f'global insert-agent {{"id":{id},"name":"{name}","ip":"{ip}","date_add":{registration_time}}}'
update_command = f'global sql UPDATE agent SET connection_status = "{connection_status}",\
disconnection_time = "{disconnection_time}" WHERE id = {id};'
try:
query_wdb(insert_command)
query_wdb(update_command)
except Exception:
raise Exception(f"Unable to add agent {id}")



@pytest.fixture(scope='function')
def insert_pre_existent_agents(get_current_test_case, stop_authd_function):
agents = get_current_test_case.get('pre_existent_agents', [])
time_now = int(time.time())
try:
keys_file = open(CLIENT_KEYS_PATH, 'w')
except IOError as exception:
raise exception

clean_agents_from_db()

for agent in agents:
id = agent['id'] if 'id' in agent else '001'
name = agent['name'] if 'name' in agent else f'TestAgent{id}'
ip = agent['ip'] if 'ip' in agent else 'any'
key = agent['key'] if 'key' in agent else 'TopSecret'
connection_status = agent['connection_status'] if 'connection_status' in agent else 'never_connected'
if 'disconnection_time' in agent and 'delta' in agent['disconnection_time']:
disconnection_time = time_now + agent['disconnection_time']['delta']
elif 'disconnection_time' in agent and 'value' in agent['disconnection_time']:
disconnection_time = agent['disconnection_time']['value']
else:
disconnection_time = time_now
if 'registration_time' in agent and 'delta' in agent['registration_time']:
registration_time = time_now + agent['registration_time']['delta']
elif 'registration_time' in agent and 'value' in agent['registration_time']:
registration_time = agent['registration_time']['value']
else:
registration_time = time_now

# Write agent in client.keys
keys_file.write(f'{id} {name} {ip} {key}\n')

# Write agent in global.db
insert_agent_in_db(id, name, ip, registration_time, connection_status, disconnection_time)

keys_file.close()
Comment on lines +161 to +197
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixtures should be placed in test_authd conftest.

1 change: 1 addition & 0 deletions deps/wazuh_testing/wazuh_testing/tools/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ def check_daemon_status(target_daemon=None, running_condition=True, timeout=10,
elapsed_time = 0

while elapsed_time < timeout and not condition_met:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

control_status_output = subprocess.run([f'{WAZUH_PATH}/bin/wazuh-control', 'status'],
stdout=subprocess.PIPE).stdout.decode()
condition_met = True
Expand Down
11 changes: 8 additions & 3 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,19 @@
###############################
report_files = [LOG_FILE_PATH, WAZUH_CONF, WAZUH_LOCAL_INTERNAL_OPTIONS]


def set_report_files(files):
if files:
for file in files:
report_files.append(file)


def get_report_files():
return report_files

###############################


def pytest_runtest_setup(item):
# Find if platform applies
supported_platforms = PLATFORMS.intersection(mark.name for mark in item.iter_markers())
Expand Down Expand Up @@ -279,11 +282,12 @@ def pytest_configure(config):
# Set files to add to the HTML report
set_report_files(config.getoption("--save-file"))

# Set WPK package path
# Set WPK package path
global_parameters.wpk_package_path = config.getoption("--wpk_package_path")
if global_parameters.wpk_package_path:
global_parameters.wpk_package_path = global_parameters.wpk_package_path


def pytest_html_results_table_header(cells):
cells.insert(4, html.th('Tier', class_='sortable tier', col='tier'))
cells.insert(3, html.th('Markers'))
Expand Down Expand Up @@ -641,7 +645,7 @@ def configure_sockets_environment_function(request):

# Stop wazuh-service and ensure all daemons are stopped
control_service('stop')
check_daemon_status(running=False)
check_daemon_status(running_condition=False)

monitored_sockets = list()
mitm_list = list()
Expand Down Expand Up @@ -727,6 +731,7 @@ def create_file_structure_function(get_files_list):

delete_file_structure(get_files_list)


@pytest.fixture(scope='module')
def configure_local_internal_options_module(request):
"""Fixture to configure the local internal options file.
Expand Down Expand Up @@ -827,7 +832,7 @@ def daemons_handler(get_configuration, request):
def file_monitoring(request):
"""Fixture to handle the monitoring of a specified file.

It uses de variable `file_to_monitor` to determinate the file to monitor. Default `LOG_FILE_PATH`
It uses the variable `file_to_monitor` to determinate the file to monitor. Default `LOG_FILE_PATH`

Args:
request (fixture): Provide information on the executing test function.
Expand Down
49 changes: 0 additions & 49 deletions tests/integration/test_authd/authd.py

This file was deleted.

Loading