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

32 import device export file from matrix #53

Merged
merged 6 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
75 changes: 75 additions & 0 deletions config/csv_mapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"name": {
"DEFAULT": "name",
"SET": null
},
"DeviceRoleCriteria": {
"DEFAULT": "OU",
"SET": null,
"MAPPING": true
},
"objectClass": {
"DEFAULT": "objectClass",
"SET": null
},
"appl-NAC-DeviceRoleProd": {
"DEFAULT": "appl-NAC-DeviceRoleProd",
"SET": null
},
"appl-NAC-DeviceRoleInst": {
"DEFAULT": "appl-NAC-DeviceRoleInst",
"SET": null
},
"appl-NAC-FQDN": {
"DEFAULT": "appl-NAC-FQDN",
"SET": null
},
"appl-NAC-Hostname": {
"DEFAULT": "appl-NAC-Hostname",
"SET": null
},
"appl-NAC-Active": {
"DEFAULT": "appl-NAC-Active",
"SET": null
},
"appl-NAC-ForceDot1X": {
"DEFAULT": "appl-NAC-ForceDot1X",
"SET": null
},
"appl-NAC-Install": {
"DEFAULT": "appl-NAC-Install",
"SET": null
},
"appl-NAC-AllowAccessCAB": {
"DEFAULT": "appl-NAC-AllowAccessCAB",
"SET": null
},
"appl-NAC-AllowAccessAIR": {
"DEFAULT": "appl-NAC-AllowAccessAIR",
"SET": null
},
"appl-NAC-AllowAccessVPN": {
"DEFAULT": "appl-NAC-AllowAccessVPN",
"SET": null
},
"appl-NAC-AllowAccessCEL": {
"DEFAULT": "appl-NAC-AllowAccessCEL",
"SET": null
},
"appl-NAC-macAddressCAB": {
"DEFAULT": "appl-NAC-macAddressCAB",
"SET": null
},
"appl-NAC-macAddressAIR": {
"DEFAULT": "appl-NAC-macAddressAIR",
"SET": null
},
"appl-NAC-Certificate": {
"DEFAULT": "appl-NAC-Certificate",
"SET": null
},
"synchronized": {
"DEFAULT": "synchronized",
"SET": null
}
}
14 changes: 14 additions & 0 deletions config/ou_mapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"DEFAULT": {
"DeviceRoleProd": "DeviceRoleProdDefault",
"DeviceRoleInst": "DeviceRoleInstDefault"
},
"OU1": {
"DeviceRoleProd": "DeviceRoleProd1",
"DeviceRoleInst": "DeviceRoleInst1"
},
"OU2": {
"DeviceRoleProd": "DeviceRoleProd2",
"DeviceRoleInst": "DeviceRoleInst2"
}
}
7 changes: 4 additions & 3 deletions resources/ldapObjects.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
appl-NAC-AllowAccessAIR;appl-NAC-AllowAccessVPN;objectClass;appl-NAC-Active;appl-NAC-AllowAccessCAB;appl-NAC-DeviceRoleInst;appl-NAC-DeviceRoleProd;appl-NAC-AllowAccessCEL;appl-NAC-Install;appl-NAC-macAddressAIR;appl-NAC-FQDN;appl-NAC-Hostname;appl-NAC-macAddressCAB;appl-NAC-Certificate;appl-NAC-ForceDot1X
True;True;appl-NAC-Device;True;True;DeviceRoleInst3;DeviceRoleProd2;True;True;001122334455;FQDN2;Hostname2;001122334455,001122334455;Certificate3;True
True;True;appl-NAC-Device;True;True;DeviceRoleInst3;DeviceRoleProd2;True;True;001122334456;FQDN4;Hostname4;001122334456,001122334456;Certificate4;True
appl-NAC-AllowAccessAIR;appl-NAC-AllowAccessVPN;objectClass;appl-NAC-Active;appl-NAC-AllowAccessCAB;appl-NAC-DeviceRoleInst;appl-NAC-DeviceRoleProd;appl-NAC-AllowAccessCEL;appl-NAC-Install;appl-NAC-macAddressAIR;appl-NAC-FQDN;appl-NAC-Hostname;appl-NAC-macAddressCAB;appl-NAC-Certificate;appl-NAC-ForceDot1X;OU
True;True;appl-NAC-Device;True;True;DeviceRoleInst2;DeviceRoleProd2;True;True;001122334455;FQDN2;Hostname1;001122334456,001122334456;Certificate3;True;OU1
True;True;appl-NAC-Device;True;True;DeviceRoleInstDefault;DeviceRoleProdDefault;True;True;001122334457;FQDN4;Hostname2;001122334458,001122334458;Certificate4;True;OU2
True;True;appl-NAC-Device;True;True;DeviceRoleInst1;DeviceRoleProd1;True;True;001122334459;FQDN4;Hostname3;001122334451,001122334451;Certificate4;True;OUDEFAULT
12 changes: 12 additions & 0 deletions src/helper/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
import logging
import json
from pathlib import Path
from configparser import ConfigParser

Expand All @@ -32,3 +33,14 @@ def get_config_from_file(file_path):
config = ConfigParser()
config.read(file_path)
return config


def get_config_from_json(file_path):
logging.debug('mapping file: {}'.format(file_path))
file_path = Path(file_path)
if not file_path.exists():
raise ConfigFileNotFound(file_path)

with open(file_path, 'r') as file:
json_config = json.load(file)
return json_config
146 changes: 98 additions & 48 deletions src/nac/management/commands/import_devices_from_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
from nac.models import Device, AuthorizationGroup, DeviceRoleProd, DeviceRoleInst
from nac.forms import DeviceForm
from helper.logging import setup_console_logger
from helper.filesystem import get_resources_directory, get_existing_path
from helper.filesystem import get_resources_directory, get_existing_path, get_config_directory
from helper.config import get_config_from_json
from helper.database import MacList
import traceback


DEFAULT_SOURCE_FILE = get_resources_directory() / 'ldapObjects.csv'
SAVE_FILE = get_resources_directory() / "invalid_devices.csv"
DEFAULT_SAVE_FILE = get_resources_directory() / "invalid_devices.csv"
DEFAULT_CSV_MAPPING = get_config_directory() / 'csv_mapping.json'
DEFAULT_OU_MAPPING = get_config_directory() / 'ou_mapping.json'


class Command(BaseCommand):
Expand All @@ -27,19 +31,29 @@ def add_arguments(self, parser):
)
parser.add_argument(
'-a', '--auth_group',
default='DefaultAG',
default='AuthGroupDefault',
help='specify the Device Authorization Group'
)
parser.add_argument(
'-u', '--update',
action='store_true',
help='specify if existing Devices should be updated'
)
parser.add_argument(
'-c', '--csv_config',
default=DEFAULT_CSV_MAPPING,
help='use a specific config file [src/csv_mapping.cfg]')
parser.add_argument(
'-o', '--ou_config',
default=DEFAULT_OU_MAPPING,
help='use a specific config file [src/ou_mapping.cfg]')

def handle(self, *args, **options):
setup_console_logger(options['verbosity'])
self.source_file = get_existing_path(options['csv_file'])
self.update = options['update']
self.csv_mapping = get_config_from_json(options['csv_config'])
self.ou_mapping = get_config_from_json(options['ou_config'])
self.mac_list = MacList()
if not self.source_file:
logging.error(
Expand All @@ -56,6 +70,12 @@ def handle(self, *args, **options):
self.clear_invalid_devices_file()
self.read_csv()

def get_set_or_default(self, json_config_key):
if json_config_key['SET'] is not None:
return json_config_key['SET']
else:
return json_config_key['DEFAULT']

def check_valid_auth_group(self, auth_group):
exists = AuthorizationGroup.objects.filter(name=auth_group).exists()
if not exists:
Expand All @@ -65,11 +85,11 @@ def check_valid_auth_group(self, auth_group):

def clear_invalid_devices_file(self):
try:
with open(SAVE_FILE, "w"):
logging.info(f"Removing all entries in {SAVE_FILE}")
with open(DEFAULT_SAVE_FILE, "w"):
logging.info(f"Removing all entries in {DEFAULT_SAVE_FILE}")
except Exception as e:
logging.error(
f"Removing all entries in {SAVE_FILE} FAILED -> {e}"
f"Removing all entries in {DEFAULT_SAVE_FILE} FAILED -> {e}"
)

def read_csv(self):
Expand All @@ -93,35 +113,65 @@ def handle_deviceObject(self, deviceObject):
self.save_invalid_devices(deviceObject)
except Exception as e:
logging.error(f"Error: Handling device Object failed -> {e}")
traceback.print_exc()

def get_deviceRole(self, deviceObject):
DeviceRoleCriteria = self.csv_mapping['DeviceRoleCriteria']
if DeviceRoleCriteria['MAPPING']:
return self.get_deviceRole_from_ou_mapping(deviceObject)
else:
return self.get_deviceRole_from_csv_mapping(deviceObject)

def get_deviceRole_from_ou_mapping(self, deviceObject):
DeviceRoleCriteria = self.csv_mapping['DeviceRoleCriteria']
ou = deviceObject.get(self.get_set_or_default(DeviceRoleCriteria))

if ou not in self.ou_mapping.keys():
ou = self.ou_mapping["DEFAULT"]
else:
ou = self.ou_mapping[ou]

try:
deviceRoleProd = DeviceRoleProd.objects.get(name=ou['DeviceRoleProd'])
except ObjectDoesNotExist:
raise ValidationError(f"DeviceRoleProd: {ou['DeviceRoleProd']} not in Database")

try:
deviceRoleInst = DeviceRoleInst.objects.get(name=ou['DeviceRoleInst'])
except ObjectDoesNotExist:
raise ValidationError(f"DeviceRoleInst: {ou['DeviceRoleInst']} not in Database")

return deviceRoleProd, deviceRoleInst

def get_deviceRole_from_csv_mapping(self, deviceObject):
deviceRoleProd = self.get_set_or_default(self.csv_mapping['appl-NAC-DeviceRoleProd'])
deviceRoleInst = self.get_set_or_default(self.csv_mapping['appl-NAC-DeviceRoleInst'])

try:
deviceRoleProd = DeviceRoleProd.objects.get(name=deviceObject.get(deviceRoleProd))
except ObjectDoesNotExist:
raise ValidationError(f"DeviceRoleProd: {deviceObject.get(deviceRoleProd)} not in Database")

try:
deviceRoleInst = DeviceRoleInst.objects.get(name=deviceObject.get(deviceRoleInst))
except ObjectDoesNotExist:
raise ValidationError(f"DeviceRoleInst: {deviceObject.get(deviceRoleInst)} not in Database")

return deviceRoleProd, deviceRoleInst

def check_device(self, deviceObject):
logging.info(f"Checking validity of device "
f"{deviceObject.get('appl-NAC-Hostname')}")
f"{deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-Hostname']))}")
try:
if deviceObject.get('objectClass') != 'appl-NAC-Device':
if deviceObject.get(self.get_set_or_default(self.csv_mapping['objectClass'])) != 'appl-NAC-Device':
raise ValidationError(
f"Invalid Object-type! EXPECTED: appl-NAC-Device <->"
f" ACTUAL: {deviceObject.get('objectClass')}")
f" ACTUAL: {deviceObject.get(self.get_set_or_default(self.csv_mapping['objectClass']))}")
with transaction.atomic():
auth_group = AuthorizationGroup.objects.get(
name=self.auth_group
)
try:
deviceRoleProd = DeviceRoleProd.objects.get(
name=deviceObject.get("appl-NAC-DeviceRoleProd"))
except ObjectDoesNotExist:
raise ValidationError(
f"DeviceRoleProd: "
f"{deviceObject.get('appl-NAC-DeviceRoleProd')} "
f"not in Database")
try:
deviceRoleInst = DeviceRoleInst.objects.get(
name=deviceObject.get("appl-NAC-DeviceRoleInst"))
except ObjectDoesNotExist:
raise ValidationError(
f"DeviceRoleInst: "
f"{deviceObject.get('appl-NAC-DeviceRoleInst')} "
f"not in Database")
deviceRoleProd, deviceRoleInst = self.get_deviceRole(deviceObject)
if deviceRoleProd not in auth_group.DeviceRoleProd.all():
raise ValidationError(
f"DeviceRoleProd: {deviceRoleProd} "
Expand All @@ -132,58 +182,58 @@ def check_device(self, deviceObject):
f"not in authorization group: {auth_group}")

device_data = {
"name": deviceObject.get("appl-NAC-Hostname"),
"name": deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-Hostname'])),
"authorization_group": auth_group,
"appl_NAC_DeviceRoleProd": deviceRoleProd,
"appl_NAC_DeviceRoleInst": deviceRoleInst,
"appl_NAC_FQDN": deviceObject.get("appl-NAC-FQDN"),
"appl_NAC_Hostname": deviceObject.get("appl-NAC-Hostname"),
"appl_NAC_FQDN": deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-FQDN'])),
"appl_NAC_Hostname": deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-Hostname'])),
"appl_NAC_Active": self.str_to_bool(
deviceObject.get("appl-NAC-Active")
deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-Active']))
),
"appl_NAC_ForceDot1X": self.str_to_bool(
deviceObject.get("appl-NAC-ForceDot1X")
deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-ForceDot1X']))
),
"appl_NAC_Install": self.str_to_bool(
deviceObject.get("appl-NAC-Install")
deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-Install']))
),
"appl_NAC_AllowAccessCAB": self.str_to_bool(
deviceObject.get("appl-NAC-AllowAccessCAB")
deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-AllowAccessCAB']))
),
"appl_NAC_AllowAccessAIR": self.str_to_bool(
deviceObject.get("appl-NAC-AllowAccessAIR")
deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-AllowAccessAIR']))
),
"appl_NAC_AllowAccessVPN": self.str_to_bool(
deviceObject.get("appl-NAC-AllowAccessVPN")
deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-AllowAccessVPN']))
),
"appl_NAC_AllowAccessCEL": self.str_to_bool(
deviceObject.get("appl-NAC-AllowAccessCEL")
deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-AllowAccessCEL']))
),
"appl_NAC_macAddressAIR": deviceObject.get(
"appl-NAC-macAddressAIR"
self.get_set_or_default(self.csv_mapping['appl-NAC-macAddressAIR'])
),
"appl_NAC_macAddressCAB": deviceObject.get(
"appl-NAC-macAddressCAB"
self.get_set_or_default(self.csv_mapping['appl-NAC-macAddressCAB'])
),
"appl_NAC_Certificate": deviceObject.get(
"appl-NAC-Certificate"
self.get_set_or_default(self.csv_mapping['appl-NAC-Certificate'])
),
"synchronized": self.str_to_bool(
deviceObject.get("synchronized")
deviceObject.get(self.get_set_or_default(self.csv_mapping['synchronized']))
)
}
device_form = DeviceForm(device_data)
if device_form.is_valid():
logging.debug(f"Device {device_data.get('name')} is valid")
exists, device_id = self.mac_list.check_existing_mac(device_form.cleaned_data)
if exists:
logging.debug(f"Device {deviceObject.get('appl-NAC-Hostname')} already exists")
logging.debug(f"Device {device_data.get('appl_NAC_Hostname')} already exists")
if self.update:
logging.debug(f"Updating Device {deviceObject.get('appl-NAC-Hostname')}")
logging.debug(f"Updating Device {device_data.get('appl_NAC_Hostname')}")
Device.objects.filter(id=device_id).update(**device_form.cleaned_data)
return None
else:
raise ValidationError(f"Device {deviceObject.get('appl-NAC-Hostname')} exists and will not get updated")
raise ValidationError(f"Device {device_data.get('appl_NAC_Hostname')} exists and will not get updated")
return device_form.cleaned_data
else:
logging.error(
Expand All @@ -198,7 +248,7 @@ def check_device(self, deviceObject):
raise
except Exception as e:
logging.error(
f"Checking validity of device {deviceObject.get('name')}: "
f"Checking validity of device {deviceObject.get(self.get_set_or_default(self.csv_mapping['appl-NAC-Hostname']))}: "
f"FAILED -> {e}"
)
raise
Expand Down Expand Up @@ -229,20 +279,20 @@ def add_device_to_db(self, deviceObject_valid):
def save_invalid_devices(self, deviceObject_invalid):
try:
column_header = deviceObject_invalid.keys()
with open(SAVE_FILE, 'a', newline="") as csvfile:
logging.info(f"Writing invalid device to {SAVE_FILE}")
with open(DEFAULT_SAVE_FILE, 'a', newline="") as csvfile:
logging.info(f"Writing invalid device to {DEFAULT_SAVE_FILE}")
writer = DictWriter(
csvfile, fieldnames=column_header, delimiter=";"
)
if stat(SAVE_FILE).st_size == 0:
if stat(DEFAULT_SAVE_FILE).st_size == 0:
writer.writeheader()
writer.writerows([deviceObject_invalid])
logging.debug(
f"Writing invalid device to {SAVE_FILE}: "
f"Writing invalid device to {DEFAULT_SAVE_FILE}: "
f"SUCCESSFUL"
)
except Exception as e:
logging.error(
f"Writing invalid device to "
f"{SAVE_FILE}: FAILED -> {e}"
f"{DEFAULT_SAVE_FILE}: FAILED -> {e}"
)
Loading
Loading