Commit 297a524fd43fbd3e916a49042777b42bb8862162

Authored by Eduardo Santos
2 parents bc60b941 d8fe80a0
Exists in master

Persistência dos ativos de rede num banco local

cocar/__init__.py
@@ -7,7 +7,6 @@ import ConfigParser @@ -7,7 +7,6 @@ import ConfigParser
7 import logging 7 import logging
8 import logging.config 8 import logging.config
9 from sqlalchemy.engine import create_engine 9 from sqlalchemy.engine import create_engine
10 -from sqlalchemy.ext.declarative import declarative_base  
11 from sqlalchemy.orm import scoped_session, sessionmaker 10 from sqlalchemy.orm import scoped_session, sessionmaker
12 11
13 12
@@ -46,11 +45,8 @@ class Cocar(object): @@ -46,11 +45,8 @@ class Cocar(object):
46 # SQLAlchemy 45 # SQLAlchemy
47 sqlalchemy_url = self.config.get('sqlalchemy', 'url') 46 sqlalchemy_url = self.config.get('sqlalchemy', 'url')
48 self.engine = create_engine(sqlalchemy_url, echo=True) 47 self.engine = create_engine(sqlalchemy_url, echo=True)
49 - self.Base = declarative_base()  
50 - self.Base.metadata.bind = self.engine  
51 - self.session = scoped_session( 48 + self.Session = scoped_session(
52 sessionmaker(bind=self.engine, 49 sessionmaker(bind=self.engine,
53 - autocommit=True,  
54 - #expire_on_commit=False  
55 - )  
56 - ) 50 + autocommit=True
  51 + )
  52 + )
57 \ No newline at end of file 53 \ No newline at end of file
cocar/model/__init__.py
1 #!/bin/env python 1 #!/bin/env python
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 -__author__ = 'eduardo'  
4 \ No newline at end of file 3 \ No newline at end of file
  4 +__author__ = 'eduardo'
  5 +
  6 +from sqlalchemy.ext.declarative import declarative_base
  7 +
  8 +Base = declarative_base()
5 \ No newline at end of file 9 \ No newline at end of file
cocar/model/computer.py
1 #!/bin/env python 1 #!/bin/env python
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 __author__ = 'eduardo' 3 __author__ = 'eduardo'
4 - 4 +from sqlalchemy.schema import Column
  5 +from sqlalchemy.types import *
  6 +from sqlalchemy import ForeignKey
5 from .host import Host 7 from .host import Host
6 8
7 9
@@ -9,6 +11,12 @@ class Computer(Host): @@ -9,6 +11,12 @@ class Computer(Host):
9 """ 11 """
10 Ativo de rede identificado como estação de trabalho 12 Ativo de rede identificado como estação de trabalho
11 """ 13 """
  14 + __tablename__ = 'computador'
  15 + network_ip = Column(String(16), ForeignKey("host.network_ip"), nullable=False, primary_key=True)
  16 + so_name = Column(String)
  17 + so_version = Column(String)
  18 + accuracy = Column(Integer)
  19 +
12 def __init__(self, 20 def __init__(self,
13 so, 21 so,
14 *args, 22 *args,
@@ -19,4 +27,10 @@ class Computer(Host): @@ -19,4 +27,10 @@ class Computer(Host):
19 :param so: Sistema Operacional encontrado 27 :param so: Sistema Operacional encontrado
20 """ 28 """
21 Host.__init__(self, *args, **kwargs) 29 Host.__init__(self, *args, **kwargs)
22 - self.so = so  
23 \ No newline at end of file 30 \ No newline at end of file
  31 + self.so = so
  32 +
  33 + #SQLAlchemy parameters
  34 + os_elm = self.so.items()[0]
  35 + self.so_name = os_elm[1]['osfamily']
  36 + self.so_version = os_elm[1]['version']
  37 + self.accuracy = os_elm[1]['accuracy']
24 \ No newline at end of file 38 \ No newline at end of file
cocar/model/host.py
@@ -2,12 +2,23 @@ @@ -2,12 +2,23 @@
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 __author__ = 'eduardo' 3 __author__ = 'eduardo'
4 from netaddr import IPAddress 4 from netaddr import IPAddress
  5 +from sqlalchemy.schema import Column
  6 +from sqlalchemy.types import String, Integer
  7 +from . import Base
5 8
6 9
7 -class Host(object): 10 +class Host(Base):
8 """ 11 """
9 Classe que define um ativo de rede 12 Classe que define um ativo de rede
10 """ 13 """
  14 + __tablename__ = 'host'
  15 + network_ip = Column(String(16), primary_key=True, nullable=False)
  16 + mac_address = Column(String(18))
  17 + name = Column(String)
  18 + inclusion_date = Column(String(20))
  19 + scantime = Column(Integer)
  20 + ports = Column(String)
  21 +
11 def __init__(self, 22 def __init__(self,
12 ip_address, 23 ip_address,
13 mac_address=None, 24 mac_address=None,
@@ -32,4 +43,25 @@ class Host(object): @@ -32,4 +43,25 @@ class Host(object):
32 self.hostname = hostname 43 self.hostname = hostname
33 self.inclusion_date = inclusion_date 44 self.inclusion_date = inclusion_date
34 self.scantime = scantime 45 self.scantime = scantime
35 - self.open_ports = open_ports  
36 \ No newline at end of file 46 \ No newline at end of file
  47 + self.open_ports = open_ports
  48 +
  49 + # Parâmetros do SQLAlchemy
  50 + self.network_ip = str(self.ip_address)
  51 + self.ports = ','.join(map(str, self.open_ports.keys()))
  52 + if len(self.hostname.values()) > 0:
  53 + self.name = self.hostname.values()[0]
  54 + else:
  55 + self.name = None
  56 +
  57 + def __repr__(self):
  58 + """
  59 + Metodo que passa a lista de parametros da classe
  60 + """
  61 + return "<Host('%s, %s, %s, %s, %s, %s')>" % (
  62 + self.network_ip,
  63 + self.mac_address,
  64 + self.name,
  65 + self.inclusion_date,
  66 + self.scantime,
  67 + self.ports
  68 + )
37 \ No newline at end of file 69 \ No newline at end of file
cocar/model/network.py
@@ -4,14 +4,25 @@ __author__ = &#39;eduardo&#39; @@ -4,14 +4,25 @@ __author__ = &#39;eduardo&#39;
4 import os.path 4 import os.path
5 from .. import Cocar 5 from .. import Cocar
6 from netaddr import IPNetwork, IPSet 6 from netaddr import IPNetwork, IPSet
  7 +from ..model import Base
  8 +from sqlalchemy.schema import Column
  9 +from sqlalchemy.types import String, Integer
7 10
8 11
9 -class Network(Cocar): 12 +class Network(Base):
10 """ 13 """
11 Rede onde a busca será realizada 14 Rede onde a busca será realizada
12 """ 15 """
  16 + __tablename__ = 'network'
  17 + ip_network = Column(String(16), nullable=False, primary_key=True)
  18 + network_file = Column(String)
  19 + netmask = Column(String(16))
  20 + prefixlen = Column(Integer)
  21 + name = Column(String)
  22 +
13 def __init__(self, 23 def __init__(self,
14 network_ip, 24 network_ip,
  25 + network_file=None,
15 netmask=None, 26 netmask=None,
16 prefixlen=None, 27 prefixlen=None,
17 name=None 28 name=None
@@ -22,17 +33,19 @@ class Network(Cocar): @@ -22,17 +33,19 @@ class Network(Cocar):
22 :param cidr: CIDR para calcular a máscara da rede 33 :param cidr: CIDR para calcular a máscara da rede
23 :param name: Nome da rede 34 :param name: Nome da rede
24 """ 35 """
25 - Cocar.__init__(self)  
26 self.network_ip = IPNetwork(network_ip) 36 self.network_ip = IPNetwork(network_ip)
27 self.netmask = netmask 37 self.netmask = netmask
28 self.prefixlen = prefixlen 38 self.prefixlen = prefixlen
29 self.name = name 39 self.name = name
30 - self.network_file = self.cocar_data_dir + "/" + str(self.network_ip.ip) + ".xml" 40 + self.network_file = network_file
31 if self.netmask is None: 41 if self.netmask is None:
32 self.netmask = self.network_ip.netmask 42 self.netmask = self.network_ip.netmask
33 if self.prefixlen is None: 43 if self.prefixlen is None:
34 self.prefixlen = self.network_ip.prefixlen 44 self.prefixlen = self.network_ip.prefixlen
35 45
  46 + # SQLAlchemy attribute
  47 + self.ip_network = str(self.network_ip.ip)
  48 +
36 def ip_list(self): 49 def ip_list(self):
37 """ 50 """
38 Método que encontra a lista de IP's da subrede 51 Método que encontra a lista de IP's da subrede
cocar/model/printer.py
@@ -3,16 +3,26 @@ @@ -3,16 +3,26 @@
3 __author__ = 'eduardo' 3 __author__ = 'eduardo'
4 4
5 from .host import Host 5 from .host import Host
  6 +from sqlalchemy import ForeignKey
  7 +from sqlalchemy.schema import Column
  8 +from sqlalchemy.types import String, Integer
6 9
7 10
8 class Printer(Host): 11 class Printer(Host):
9 """ 12 """
10 Classe que identifica uma impressora 13 Classe que identifica uma impressora
11 """ 14 """
  15 + __tablename__ = 'printer'
  16 + network_ip = Column(String(16), ForeignKey("host.network_ip"), nullable=False, primary_key=True)
  17 + counter = Column(Integer)
  18 + serial = Column(String(50))
  19 + description = Column(String)
  20 +
12 def __init__(self, 21 def __init__(self,
13 counter=None, 22 counter=None,
14 model=None, 23 model=None,
15 serial=None, 24 serial=None,
  25 + description=None,
16 *args, 26 *args,
17 **kwargs 27 **kwargs
18 ): 28 ):
@@ -24,4 +34,5 @@ class Printer(Host): @@ -24,4 +34,5 @@ class Printer(Host):
24 Host.__init__(self, *args, **kwargs) 34 Host.__init__(self, *args, **kwargs)
25 self.counter = counter 35 self.counter = counter
26 self.model = model 36 self.model = model
27 - self.serial = serial  
28 \ No newline at end of file 37 \ No newline at end of file
  38 + self.serial = serial
  39 + self.description = description
29 \ No newline at end of file 40 \ No newline at end of file
cocar/tests/__init__.py
@@ -4,6 +4,7 @@ __author__ = &#39;eduardo&#39; @@ -4,6 +4,7 @@ __author__ = &#39;eduardo&#39;
4 from .. import Cocar 4 from .. import Cocar
5 import os 5 import os
6 import os.path 6 import os.path
  7 +from ..model import Base
7 8
8 cocar = Cocar(environment='test') 9 cocar = Cocar(environment='test')
9 test_dir = os.path.dirname(os.path.realpath(__file__)) 10 test_dir = os.path.dirname(os.path.realpath(__file__))
@@ -13,7 +14,7 @@ def setup_package(): @@ -13,7 +14,7 @@ def setup_package():
13 """ 14 """
14 Setup test data for the package 15 Setup test data for the package
15 """ 16 """
16 - cocar.Base.metadata.create_all(cocar.engine) 17 + Base.metadata.create_all(cocar.engine)
17 pass 18 pass
18 19
19 20
@@ -21,5 +22,5 @@ def teardown_package(): @@ -21,5 +22,5 @@ def teardown_package():
21 """ 22 """
22 Remove test data 23 Remove test data
23 """ 24 """
24 - cocar.Base.metadata.drop_all(cocar.engine) 25 + Base.metadata.drop_all(cocar.engine)
25 pass 26 pass
26 \ No newline at end of file 27 \ No newline at end of file
cocar/tests/test_discover.py
@@ -74,7 +74,7 @@ class TestDiscover(unittest.TestCase): @@ -74,7 +74,7 @@ class TestDiscover(unittest.TestCase):
74 self.assertTrue(os.path.isfile(session.outfile)) 74 self.assertTrue(os.path.isfile(session.outfile))
75 75
76 # Apaga arquivo 76 # Apaga arquivo
77 - os.unlink(session.outfile) 77 + #os.unlink(session.outfile)
78 78
79 def test_scan_rede(self): 79 def test_scan_rede(self):
80 """ 80 """
cocar/tests/test_identify.py
@@ -60,8 +60,9 @@ class TestIdentify(unittest.TestCase): @@ -60,8 +60,9 @@ class TestIdentify(unittest.TestCase):
60 self.assertIsInstance(computer, Computer) 60 self.assertIsInstance(computer, Computer)
61 61
62 # Se é um computer, tenho que identificar o SO 62 # Se é um computer, tenho que identificar o SO
63 - os_elm = computer.so.items()[0]  
64 - self.assertEqual(os_elm[1]['osfamily'], 'Linux') 63 + self.assertEqual(computer.so_name, 'Linux')
  64 + self.assertEqual(computer.so_version, 'Linux 3.7 - 3.9')
  65 + self.assertEqual(computer.accuracy, '98')
65 66
66 def test_identify_printer(self): 67 def test_identify_printer(self):
67 """ 68 """
cocar/tests/test_persistence.py
@@ -6,6 +6,8 @@ import unittest @@ -6,6 +6,8 @@ import unittest
6 import cocar.tests 6 import cocar.tests
7 from ..xml_utils import NmapXML 7 from ..xml_utils import NmapXML
8 from ..model.computer import Computer 8 from ..model.computer import Computer
  9 +from ..model.printer import Printer
  10 +from ..model.network import Network
9 11
10 12
11 class TestPersistence(unittest.TestCase): 13 class TestPersistence(unittest.TestCase):
@@ -20,12 +22,13 @@ class TestPersistence(unittest.TestCase): @@ -20,12 +22,13 @@ class TestPersistence(unittest.TestCase):
20 self.network_file = cocar.tests.test_dir + "/fixtures/192.168.0.0-24.xml" 22 self.network_file = cocar.tests.test_dir + "/fixtures/192.168.0.0-24.xml"
21 self.localhost_file = cocar.tests.test_dir + "/fixtures/127.0.0.1.xml" 23 self.localhost_file = cocar.tests.test_dir + "/fixtures/127.0.0.1.xml"
22 self.printer_file = cocar.tests.test_dir + "/fixtures/printer.xml" 24 self.printer_file = cocar.tests.test_dir + "/fixtures/printer.xml"
  25 + self.session = cocar.tests.cocar.Session
23 26
24 def test_connect(self): 27 def test_connect(self):
25 """ 28 """
26 Testa conexão do SQLAlchemy 29 Testa conexão do SQLAlchemy
27 """ 30 """
28 - db_session = cocar.tests.cocar.session 31 + db_session = self.session
29 self.assertIsNotNone(db_session) 32 self.assertIsNotNone(db_session)
30 33
31 def test_persist_computer(self): 34 def test_persist_computer(self):
@@ -40,8 +43,54 @@ class TestPersistence(unittest.TestCase): @@ -40,8 +43,54 @@ class TestPersistence(unittest.TestCase):
40 computer = nmap_xml.identify_host(hostname) 43 computer = nmap_xml.identify_host(hostname)
41 self.assertIsInstance(computer, Computer) 44 self.assertIsInstance(computer, Computer)
42 45
  46 + # Agora testa a persistência
  47 + self.session.add(computer)
  48 + self.session.flush()
  49 +
  50 + # Tenta ver se gravou
  51 + results = self.session.query(Computer).first()
  52 + self.assertIsNotNone(results)
  53 +
  54 + def test_persist_printer(self):
  55 + """
  56 + Grava impressora no banco de dados
  57 + """
  58 + hostname = '10.72.168.3'
  59 + nmap_xml = NmapXML(self.printer_file)
  60 + host = nmap_xml.parse_xml()
  61 + assert host
  62 +
  63 + printer = nmap_xml.identify_host(hostname)
  64 + self.assertIsInstance(printer, Printer)
  65 +
  66 + # Agora testa a persistência
  67 + self.session.add(printer)
  68 + self.session.flush()
  69 +
  70 + # Tenta ver se gravou
  71 + results = self.session.query(Printer).first()
  72 + self.assertIsNotNone(results)
  73 +
  74 + def test_persist_network(self):
  75 + """
  76 + Testa gravação dos dados de rede
  77 + """
  78 + rede = Network(
  79 + network_ip='192.168.0.0',
  80 + netmask='255.255.255.0',
  81 + network_file='/tmp/network.xml',
  82 + name='Rede de Teste'
  83 + )
  84 + self.session.add(rede)
  85 + self.session.flush()
  86 +
  87 + # Tenta ver se gravou
  88 + results = self.session.query(Network).first()
  89 + self.assertIsNotNone(results)
  90 +
43 def tearDown(self): 91 def tearDown(self):
44 """ 92 """
45 Remove dados 93 Remove dados
46 """ 94 """
  95 + self.session.close()
47 pass 96 pass
48 \ No newline at end of file 97 \ No newline at end of file
cocar/xml_utils.py
@@ -69,7 +69,8 @@ class NmapXML(object): @@ -69,7 +69,8 @@ class NmapXML(object):
69 'vendor': osclass.get('vendor'), 69 'vendor': osclass.get('vendor'),
70 'osfamily': osclass.get('osfamily'), 70 'osfamily': osclass.get('osfamily'),
71 'accuracy': osclass.get('accuracy'), 71 'accuracy': osclass.get('accuracy'),
72 - 'cpe': osclass.findtext('cpe') 72 + 'cpe': osclass.findtext('cpe'),
  73 + 'version': osmatch.get('name')
73 } 74 }
74 75
75 # General attributes 76 # General attributes
@@ -86,11 +87,12 @@ class NmapXML(object): @@ -86,11 +87,12 @@ class NmapXML(object):
86 87
87 # Ordena os sistemas operacionais por accuracy 88 # Ordena os sistemas operacionais por accuracy
88 host = self.hosts[hostname] 89 host = self.hosts[hostname]
89 - accuracy = 0 90 + accuracy = int(0)
90 if host.get('os'): 91 if host.get('os'):
91 # Nesse caso já sei que é computador. Precisa identificar o OS 92 # Nesse caso já sei que é computador. Precisa identificar o OS
92 for os in host['os'].keys(): 93 for os in host['os'].keys():
93 if int(host['os'][os]['accuracy']) > accuracy: 94 if int(host['os'][os]['accuracy']) > accuracy:
  95 + accuracy = int(host['os'][os]['accuracy'])
94 os_final = os 96 os_final = os
95 97
96 scantime = int(host.get('endtime')) - int(host.get('starttime')) 98 scantime = int(host.get('endtime')) - int(host.get('starttime'))