class WIFIPayload(Payload): id = Column(Integer, ForeignKey('payloads.id'), primary_key=True) ssid_str = Column(String, nullable=False) hidden_network = Column(Boolean, default=False) auto_join = Column(Boolean, nullable=True) encryption_type = Column(DBEnum(WIFIEncryptionType), default=WIFIEncryptionType.Any) is_hotspot = Column(Boolean) domain_name = Column(String) service_provider_roaming_enabled = Column(Boolean) roaming_consortium_ois = Column(String) # JSON nai_realm_names = Column(String) # JSON mccs_and_mncs = Column(String) # JSON displayed_operator_name = Column(String) captive_bypass = Column(Boolean) # If WEP, WPA or Any password = Column(String) #eap_client_configuration_id = Column(Integer, ForeignKey('eap_client_configurations.id')) tls_certificate_required = Column(Boolean) payload_certificate_uuid = Column(GUID) # Manual Proxy proxy_type = Column(String) proxy_server = Column(String) proxy_server_port = Column(Integer) proxy_username = Column(String) proxy_password = Column(String) proxy_pac_url = Column(String) proxy_pac_fallback_allowed = Column(Boolean) __mapper_args__ = { 'polymorphic_identity': 'com.apple.wifi.managed', }
class Profile(db.Model): """Top level profile. In Commandment, multiple profiles may have an association with the same payload. See Also: - `Configuration Profile Keys <https://developer.apple.com/library/content/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010206-CH1-SW7>`_. Attributes: """ __tablename__ = 'profiles' id = Column(Integer, primary_key=True) description = Column(Text) display_name = Column(String) expiration_date = Column(DateTime) # Only for old style OTA identifier = Column(String, nullable=False) organization = Column(String) uuid = Column(GUID, index=True, default=uuid4()) removal_disallowed = Column(Boolean) version = Column(Integer, default=1) scope = Column(DBEnum(PayloadScope), default=PayloadScope.User.value) removal_date = Column(DateTime) duration_until_removal = Column(BigInteger) consent_en = Column(Text) is_encrypted = Column(Boolean, default=False) payloads = relationship('Payload', secondary=profile_payloads, backref='profiles')
class DataStorage(JoinedComponentTableMixin, Component): """A device that stores information.""" size = Column(Integer, check_range('size', min=1, max=10**8)) size.comment = """ The size of the data-storage in MB. """ interface = Column(DBEnum(DataStorageInterface)) @property def privacy(self): """Returns the privacy compliance state of the data storage. This is, the last erasure performed to the data storage. """ from ereuse_devicehub.resources.event.models import EraseBasic try: ev = self.last_event_of(EraseBasic) except LookupError: ev = None return ev def __format__(self, format_spec): v = super().__format__(format_spec) if 's' in format_spec: v += ' – {} GB'.format(self.size // 1000 if self.size else '?') return v
class EmailPayload(Payload): """E-mail Payload""" id = Column(Integer, ForeignKey('payloads.id'), primary_key=True) email_account_description = Column(String) email_account_name = Column(String) email_account_type = Column(DBEnum(EmailAccountType), nullable=False) email_address = Column(String) incoming_auth = Column(DBEnum(EmailAuthenticationType), nullable=False) incoming_host = Column(String, nullable=False) incoming_port = Column(Integer) incoming_use_ssl = Column(Boolean, default=False) incoming_username = Column(String, nullable=False) incoming_password = Column(String) outgoing_password = Column(String) outgoing_incoming_same = Column(Boolean) outgoing_auth = Column(DBEnum(EmailAuthenticationType), nullable=False) __mapper_args__ = {'polymorphic_identity': 'com.apple.mail.managed'}
class Agent(Thing): id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4) type = Column(Unicode, nullable=False) name = Column(CIText()) name.comment = """ The name of the organization or person. """ tax_id = Column(Unicode(length=STR_SM_SIZE), check_lower('tax_id')) tax_id.comment = """ The Tax / Fiscal ID of the organization, e.g. the TIN in the US or the CIF/NIF in Spain. """ country = Column(DBEnum(enums.Country)) country.comment = """ Country issuing the tax_id number. """ telephone = Column(PhoneNumberType()) email = Column(EmailType, unique=True) __table_args__ = (UniqueConstraint( tax_id, country, name='Registration Number per country.'), UniqueConstraint(tax_id, name, name='One tax ID with one name.'), db.Index('agent_type', type, postgresql_using='hash')) @declared_attr def __mapper_args__(cls): """ Defines inheritance. From `the guide <http://docs.sqlalchemy.org/en/latest/orm/ extensions/declarative/api.html #sqlalchemy.ext.declarative.declared_attr>`_ """ args = {POLYMORPHIC_ID: cls.t} if cls.t == 'Agent': args[POLYMORPHIC_ON] = cls.type if JoinedTableMixin in cls.mro(): args[INHERIT_COND] = cls.id == Agent.id return args @property def events(self) -> list: # todo test return sorted(chain(self.events_agent, self.events_to), key=attrgetter('created')) @validates('name') def does_not_contain_slash(self, _, value: str): if '/' in value: raise ValidationError('Name cannot contain slash \'') return value def __repr__(self) -> str: return '<{0.t} {0.name}>'.format(self)
class Printer(Device): id = Column(BigInteger, ForeignKey(Device.id), primary_key=True) wireless = Column(Boolean, nullable=False, default=False) wireless.comment = """Whether it is a wireless printer.""" scanning = Column(Boolean, nullable=False, default=False) scanning.comment = """Whether the printer has scanning capabilities.""" technology = Column(DBEnum(PrinterTechnology)) technology.comment = """Technology used to print.""" monochrome = Column(Boolean, nullable=False, default=True) monochrome.comment = """Whether the printer is only monochrome."""
class VPNPayload(Payload): """VPN Payload""" id = Column(Integer, ForeignKey('payloads.id'), primary_key=True) user_defined_name = Column(String) override_primary = Column(Boolean, default=False) vpn_type = Column(DBEnum(VPNType), nullable=False) vpn_sub_type = Column(String) provider_bundle_identifier = Column(String) on_demand_enabled = Column(Integer) __mapper_args__ = { 'polymorphic_identity': 'com.apple.vpn.managed', }
class Rate(JoinedTableMixin, EventWithOneDevice): rating = Column(Float(decimal_return_scale=2), check_range('rating', *RATE_POSITIVE)) algorithm_software = Column(DBEnum(RatingSoftware), nullable=False) algorithm_version = Column(StrictVersionType, nullable=False) appearance = Column(Float(decimal_return_scale=2), check_range('appearance', *RATE_NEGATIVE)) functionality = Column(Float(decimal_return_scale=2), check_range('functionality', *RATE_NEGATIVE)) @property def rating_range(self) -> RatingRange: return RatingRange.from_score(self.rating)
class TestDataStorage(Test): id = Column(UUID(as_uuid=True), ForeignKey(Test.id), primary_key=True) length = Column(DBEnum(TestHardDriveLength), nullable=False) # todo from type status = Column(Unicode(STR_SIZE), nullable=False) lifetime = Column(Interval, nullable=False) first_error = Column(SmallInteger, nullable=False, default=0) passed_lifetime = Column(Interval) assessment = Column(Boolean) reallocated_sector_count = Column(SmallInteger) power_cycle_count = Column(SmallInteger) reported_uncorrectable_errors = Column(SmallInteger) command_timeout = Column(SmallInteger) current_pending_sector_count = Column(SmallInteger) offline_uncorrectable = Column(SmallInteger) remaining_lifetime_percentage = Column(SmallInteger)
class ADCertPayload(Payload): id = Column(Integer, ForeignKey('payloads.id'), primary_key=True) certificate_description = Column( String) # Description was reserved from the base Payload table allow_all_apps_access = Column(Boolean) cert_server = Column(String, nullable=False) cert_template = Column(String, nullable=False, default='User') acquisition_mechanism = Column( DBEnum(ADCertificateAcquisitionMechanism), default=ADCertificateAcquisitionMechanism.RPC) certificate_authority = Column(String, nullable=False) renewal_time_interval = Column(Integer) identity_description = Column(String, nullable=True) key_is_extractable = Column(Boolean, default=False) prompt_for_credentials = Column(Boolean) keysize = Column(Integer, default=2048) __mapper_args__ = { 'polymorphic_identity': 'com.apple.ADCertificate.managed', }
class Snapshot(JoinedTableMixin, EventWithOneDevice): uuid = Column(UUID(as_uuid=True), nullable=False, unique=True) version = Column(StrictVersionType(STR_SM_SIZE), nullable=False) software = Column(DBEnum(SnapshotSoftware), nullable=False) elapsed = Column(Interval, nullable=False) expected_events = Column(ArrayOfEnum(DBEnum(SnapshotExpectedEvents)))
class Keyboard(ComputerAccessory): layout = Column(DBEnum(Layouts)) # If we want to do it not null
class Laptop(Computer): layout = Column(DBEnum(Layouts)) layout.comment = """Layout of a built-in keyboard of the computer,
class Computer(Device): """A chassis with components inside that can be processed automatically with Workbench Computer. Computer is broadly extended by ``Desktop``, ``Laptop``, and ``Server``. The property ``chassis`` defines it more granularly. """ id = Column(BigInteger, ForeignKey(Device.id), primary_key=True) chassis = Column(DBEnum(ComputerChassis), nullable=False) chassis.comment = """The physical form of the computer. It is a subset of the Linux definition of DMI / DMI decode. """ def __init__(self, chassis, **kwargs) -> None: chassis = ComputerChassis(chassis) super().__init__(chassis=chassis, **kwargs) @property def events(self) -> list: return sorted(chain(super().events, self.events_parent), key=self.EVENT_SORT_KEY) @property def ram_size(self) -> int: """The total of RAM memory the computer has.""" return sum(ram.size or 0 for ram in self.components if isinstance(ram, RamModule)) @property def data_storage_size(self) -> int: """The total of data storage the computer has.""" return sum(ds.size or 0 for ds in self.components if isinstance(ds, DataStorage)) @property def processor_model(self) -> str: """The model of one of the processors of the computer.""" return next( (p.model for p in self.components if isinstance(p, Processor)), None) @property def graphic_card_model(self) -> str: """The model of one of the graphic cards of the computer.""" return next( (p.model for p in self.components if isinstance(p, GraphicCard)), None) @property def network_speeds(self) -> List[int]: """Returns two values representing the speeds of the network adapters of the device. 1. The max Ethernet speed of the computer, 0 if ethernet adaptor exists but its speed is unknown, None if no eth adaptor exists. 2. The max WiFi speed of the computer, 0 if computer has WiFi but its speed is unknown, None if no WiFi adaptor exists. """ speeds = [None, None] for net in (c for c in self.components if isinstance(c, NetworkAdapter)): speeds[net.wireless] = max(net.speed or 0, speeds[net.wireless] or 0) return speeds @property def privacy(self): """Returns the privacy of all ``DataStorage`` components when it is not None. """ return set(privacy for privacy in (hdd.privacy for hdd in self.components if isinstance(hdd, DataStorage)) if privacy) def __format__(self, format_spec): if not format_spec: return super().__format__(format_spec) v = '' if 't' in format_spec: v += '{0.chassis} {0.model}'.format(self) elif 's' in format_spec: v += '({0.manufacturer})'.format(self) if self.serial_number: v += ' S/N ' + self.serial_number.upper() return v
class DisplayMixin: """Base class for the Display Component and the Monitor Device.""" size = Column(Float(decimal_return_scale=1), check_range('size', 2, 150), nullable=False) size.comment = """ The size of the monitor in inches. """ technology = Column(DBEnum(DisplayTech)) technology.comment = """ The technology the monitor uses to display the image. """ resolution_width = Column(SmallInteger, check_range('resolution_width', 10, 20000), nullable=False) resolution_width.comment = """ The maximum horizontal resolution the monitor can natively support in pixels. """ resolution_height = Column(SmallInteger, check_range('resolution_height', 10, 20000), nullable=False) resolution_height.comment = """ The maximum vertical resolution the monitor can natively support in pixels. """ refresh_rate = Column(SmallInteger, check_range('refresh_rate', 10, 1000)) contrast_ratio = Column(SmallInteger, check_range('contrast_ratio', 100, 100000)) touchable = Column(Boolean) touchable.comment = """Whether it is a touchscreen.""" @hybrid_property def aspect_ratio(self): """The aspect ratio of the display, as a fraction: ``X/Y``. Regular values are ``4/3``, ``5/4``, ``16/9``, ``21/9``, ``14/10``, ``19/10``, ``16/10``. """ return Fraction(self.resolution_width, self.resolution_height) # noinspection PyUnresolvedReferences @aspect_ratio.expression def aspect_ratio(cls): # The aspect ratio to use as SQL in the DB # This allows comparing resolutions return db.func.round(cls.resolution_width / cls.resolution_height, 2) @hybrid_property def widescreen(self): """Whether the monitor is considered to be widescreen. Widescreen monitors are those having a higher aspect ratio greater than 4/3. """ # We add a tiny extra to 4/3 to avoid precision errors return self.aspect_ratio > 4.001 / 3 def __str__(self) -> str: return '{0.t} {0.serial_number} {0.size}in ({0.aspect_ratio}) {0.technology}'.format( self) def __format__(self, format_spec: str) -> str: v = '' if 't' in format_spec: v += '{0.t} {0.model}'.format(self) if 's' in format_spec: v += '({0.manufacturer}) S/N {0.serial_number}'.format(self) v += '– {0.size}in ({0.aspect_ratio}) {0.technology}'.format(self) return v
class MinesweeperPattern(ModelBase): success_rate: int = Column(Integer) failure_rate: int = Column(Integer) click_event: MinesweeperClickEvent = Column(DBEnum(MinesweeperClickEvent)) complexity: int = Column(Integer) _hash: int = None hashes: List[MinesweeperPatternHash] def __init__(self, click_event: MinesweeperClickEvent, success_rate: int = 0, failure_rate: int = 0): self.click_event = click_event self.success_rate = success_rate self.failure_rate = failure_rate def _get_main_value_list(self, sibling_index: int, from_tile: BoardTile) -> Tuple[any, List[any]]: value_list = self._get_tile_hash_values([], sibling_index, from_tile) return value_list.pop(0), value_list def calculate_main_hash(self, sibling_index: int, from_tile: BoardTile) -> MinesweeperPatternHash: main_value, formatted_siblings = self._get_main_value_list( sibling_index, from_tile) other_sibls = [ s for i, s in enumerate(formatted_siblings) if i != sibling_index ] hidden_sibls = len( [s for s in other_sibls if s == MinesweeperTileState.HIDDEN]) self.complexity = round(hidden_sibls / len(other_sibls), 2) main_hash = MinesweeperPatternHash( self._calculate_hash_from_value_list(sibling_index, main_value, formatted_siblings), sibling_index, self) return main_hash def calculate_other_hashes( self, from_tile: BoardTile) -> List[MinesweeperPatternHash]: if not self.hashes: raise RuntimeError('No existing main hash exists.') main_hash = self.hashes[0] sibling_index = main_hash.sibling_index main_value, formatted_siblings = self._get_main_value_list( sibling_index, from_tile) self._calculate_hashes( sibling_index, main_value, {k: v for k, v in enumerate(formatted_siblings)}) hash(self) return [h for h in self.hashes if h != main_hash] def _calculate_hash_from_value_list(self, sibling_index: int, main_value: any, value_list: List[any]) -> str: raw_hash = [ sibling_index, self.click_event.value, main_value, '-'.join([str(v) for v in value_list]) ] hash_object = hashlib.sha1('-'.join([str(h) for h in raw_hash ]).encode('utf-8')) return hash_object.hexdigest() def _calculate_hashes(self, sibling_index: int, main_value: any, sibling_values: Dict[int, any]): all_vls = [sibling_values, self._mirror_value_list(sibling_values)] for vl in all_vls.copy(): rot_1 = self._rotate_value_list_left(vl) rot_2 = self._rotate_value_list_left(rot_1) rot_3 = self._rotate_value_list_left(rot_2) all_vls.extend([rot_1, rot_2, rot_3]) all_vls.pop(0) for p in all_vls: for m_i, i in enumerate(p.keys()): if i == sibling_index: break else: raise RuntimeError('Cannot calculate mirrored index') raw_hash = self._calculate_hash_from_value_list( m_i, main_value, list(p.values())) for existing in self.hashes: if existing.hash_ == raw_hash: break else: MinesweeperPatternHash(raw_hash, m_i, self) def _mirror_value_list(self, sibling_values: Dict[int, any]) -> Dict[int, any]: sv = [(k, v) for k, v in sibling_values.items()] return dict([sv[2], sv[1], sv[0], sv[4], sv[3], sv[7], sv[6], sv[5]]) def _rotate_value_list_left( self, sibling_values: Dict[int, any]) -> Dict[int, any]: sv = [(k, v) for k, v in sibling_values.items()] return dict([sv[5], sv[3], sv[0], sv[6], sv[1], sv[7], sv[4], sv[2]]) def _get_tile_hash_values(self, value_list: List[any], sibling_index: int, tile: BoardTile) -> List[any]: self.__class__._add_tile_hash_value(value_list, tile) open_siblings = {s for s in tile.siblings if s and not s.is_displayed} for index, sibling in enumerate(tile.siblings): if not sibling: value_list.append(MinesweeperTileState.NO_TILE) elif index == sibling_index and self.click_event == MinesweeperClickEvent.UNFLAG: value_list.append(MinesweeperTileState.HIDDEN) elif ((sibling.is_displayed and sibling.value and sibling.value > tile.value) or not ({s for s in sibling.siblings}.union({sibling}) & open_siblings)): value_list.append(MinesweeperTileState.ANY_VALUE) else: self.__class__._add_tile_hash_value(value_list, sibling) return value_list @classmethod def _add_tile_hash_value(cls, value_list: List[any], tile: BoardTile) -> List[any]: if tile.is_flagged: value_list.append(MinesweeperTileState.FLAGGED) elif not tile.is_displayed: value_list.append(MinesweeperTileState.HIDDEN) else: value_list.append(tile.value) return value_list @classmethod def get_unclicked_sibling_indexes(cls, from_tile: BoardTile) -> List[int]: indexes: List[int] = [] for index, s in enumerate(from_tile.siblings): if s and not s.is_displayed: indexes.append(index) if not indexes: raise RuntimeError('No unclicked sibling could be found.') return indexes @property def confidence(self) -> float: success_rate = self.success_rate**2 failure_rate = self.failure_rate**2 total = success_rate + failure_rate if total == 1: return success_rate - failure_rate elif total > 0: co = (1 - (1 / total)) * (1 - self.complexity) confidence = (success_rate / total) - (failure_rate / total) return round(confidence * 100 * co) else: return 0 def __eq__(self, other) -> bool: if not self.__class__ is other.__class__: return False return hash(self) == hash(other) def __hash__(self) -> int: return int(''.join(sorted([h.hash_ for h in self.hashes])), 16)