class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String) fullname = db.Column(db.String) password = db.Column(db.String)
class RSAPrivateKey(db.Model): """RSA Private Key Model""" __tablename__ = 'rsa_private_keys' #: id db.Column id = db.Column(db.Integer, primary_key=True) pem_data = db.Column(db.Text, nullable=False) @classmethod def from_crypto(cls, private_key: rsa.RSAPrivateKeyWithSerialization): """Convert a cryptography RSAPrivateKey object to an SQLAlchemy model.""" # type: (type, rsa.RSAPrivateKeyWithSerialization) -> RSAPrivateKey m = cls() m.pem_data = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) return m def to_crypto(self) -> rsa.RSAPrivateKey: """Convert an SQLAlchemy RSAPrivateKey model to a cryptography RSA Private Key.""" pk = serialization.load_pem_private_key( self.pem_data, backend=default_backend(), password=None, ) return pk
class OAuth2Client(db.Model, OAuth2ClientMixin): """OAuth 2 Client""" __tablename__ = 'oauth2_clients' id = db.Column(db.Integer, primary_key=True) user_id = db.Column( db.Integer, db.ForeignKey('users.id', ondelete='CASCADE') ) user = db.relationship('User')
class OAuth2Token(db.Model, OAuth2TokenMixin): """Bearer Token""" __tablename__ = 'oauth2_tokens' id = db.Column(db.Integer, primary_key=True) user_id = db.Column( db.Integer, db.ForeignKey('users.id', ondelete='CASCADE') ) user = db.relationship('User')
class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String) fullname = db.Column(db.String) password = db.Column(db.String) def get_user_id(self): """This method is implemented as part of the Resource Owner interface for Authlib.""" return self.id
class DEPConfiguration(db.Model): __tablename__ = 'dep_configurations' id = db.Column(db.Integer, primary_key=True) # certificate for PKI of server token certificate_id = db.Column(db.ForeignKey('certificates.id')) certificate = db.relationship('DEPServerTokenCertificate', backref='dep_configurations') # OAuth creds consumer_key = db.String() consumer_secret = db.String() access_token = db.String() access_secret = db.String() url = db.String()
class InstalledCertificate(db.Model): """This model represents a single installed certificate on an enrolled device as returned by the ``CertificateList`` query. The response will usually include both certificates managed by profiles and certificates that were installed outside of a profile. :table: installed_certificates See Also: - `CertificateList Command <https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/MobileDeviceManagementProtocolRef/3-MDM_Protocol/MDM_Protocol.html#//apple_ref/doc/uid/TP40017387-CH3-SW13>`_. """ __tablename__ = 'installed_certificates' id = db.Column(db.Integer, primary_key=True) """(int): Installed Certificate ID""" device_udid = db.Column(db.String(40), index=True, nullable=False) """(GUID): Unique Device Identifier""" device_id = db.Column(db.ForeignKey('devices.id'), nullable=True) """(int): Device foreign key ID.""" device = db.relationship('Device', backref='installed_certificates') """(db.relationship): Device relationship""" x509_cn = db.Column(db.String) """(str): The X.509 Common Name of the certificate.""" is_identity = db.Column(db.Boolean) """(bool): Is the certificate an identity certificate?""" der_data = db.Column(db.LargeBinary, nullable=False) """(bytes): The DER encoded certificate data.""" fingerprint_sha256 = db.Column(db.String(64), nullable=False, index=True) """(str): SHA-256 fingerprint of the certificate."""
class InstalledPayload(db.Model): __tablename__ = 'installed_payloads' id = db.Column(db.Integer, primary_key=True) """(int): Installed Payload ID""" profile_id = db.Column(db.ForeignKey('installed_profiles.id'), nullable=False) """(int): InstalledProfile foreign key ID.""" profile = db.relationship('InstalledProfile', backref='payload_content') device_id = db.Column(db.ForeignKey('devices.id'), nullable=True) """(int): Device foreign key ID.""" device = db.relationship('Device', backref='installed_payloads') """(db.relationship): Device relationship""" """(db.relationship): InstalledProfile relationship""" description = db.Column(db.String) """(str): Payload description (value of PayloadDescription)""" display_name = db.Column(db.String) """(str): Payload display name""" identifier = db.Column(db.String) organization = db.Column(db.String) payload_type = db.Column(db.String) uuid = db.Column(GUID())
class InstalledApplication(db.Model): """This model represents a single application that was returned as part of an ``InstalledApplicationList`` query. It is impossible to create a composite key to uniquely identify each row, therefore every time the device reports back we need to wipe all rows associated with a single device. The reason why a composite key won't work here is that macOS will often report the binary name and no identifier, version, or size (and sometimes iOS can do the inverse of that). :table: installed_applications See Also: - `InstalledApplicationList Command <https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/MobileDeviceManagementProtocolRef/3-MDM_Protocol/MDM_Protocol.html#//apple_ref/doc/uid/TP40017387-CH3-SW14>`_. """ __tablename__ = 'installed_applications' id = db.Column(db.Integer, primary_key=True) """id (int): Identifier""" device_udid = db.Column(db.String(40), index=True, nullable=False) """device_udid (GUID): Unique device identifier""" device_id = db.Column(db.ForeignKey('devices.id'), nullable=True) """device_id (int): Parent relationship ID of the device""" device = db.relationship('Device', backref='installed_applications') """device (db.relationship): SQLAlchemy relationship to the device.""" # Many of these can be empty, so there is no valid composite key bundle_identifier = db.Column(db.String, index=True) """bundle_identifier (str): The com.xxx.yyy bundle identifier for the application. May be empty.""" version = db.Column(db.String, index=True) """version (str): The long version for the application. May be empty.""" short_version = db.Column(db.String) """short_version (str): The short version for the application. May be empty.""" name = db.Column(db.String) """name (str): The application name""" bundle_size = db.Column(db.BigInteger) """bundle_size (int): The application size""" dynamic_size = db.Column(db.BigInteger) """dynamic_size (int): The dynamic data size (for iOS containers).""" is_validated = db.Column(db.Boolean) """is_validated (bool):""" external_version_identifier = db.Column(db.BigInteger, index=True) """external_version_identifier (int): The application’s external version ID. It can be used for comparison in the iTunes Search API to decide if the application needs to be updated.""" adhoc_codesigned = db.Column(db.Boolean) appstore_vendable = db.Column(db.Boolean) beta_app = db.Column(db.Boolean) device_based_vpp = db.Column(db.Boolean) has_update_available = db.Column(db.Boolean) installing = db.Column(db.Boolean)
class Certificate(db.Model): """Polymorphic base for certificate types. These certificate classes are only intended to be used for storing certificates related to running the MDM or certificates issued by the MDM internal CA or SCEP service. Note that X.509 name attributes have fixed lengths as defined in `RFC5280`_. :table: certificates .. _RFC5280: http://www.ietf.org/rfc/rfc5280.txt """ __tablename__ = 'certificates' id = db.Column(db.Integer, primary_key=True) """id (int): Primary Key""" pem_data = db.Column(db.Text, nullable=False) """pem_data (str): PEM Encoded Certificate Data""" rsa_private_key_id = db.Column(db.Integer, db.ForeignKey('rsa_private_keys.id')) """rsa_private_key_id (int): Foreign key reference to an RSAPrivateKey IF the private key was generated by us.""" rsa_private_key = db.relationship( 'RSAPrivateKey', backref='certificates', ) x509_cn = db.Column(db.String(64), nullable=True) """x509_cn (str): X.509 Common Name""" x509_ou = db.Column(db.String(32)) """x509_ou (str): X.509 Organizational Unit""" x509_o = db.Column(db.String(64)) """x509_o (str): X.509 Organization""" x509_c = db.Column(db.String(2)) """x509_c (str): X.509 2 letter Country Code""" x509_st = db.Column(db.String(128)) """x509_st (str): X.509 State or Location""" not_before = db.Column(db.DateTime(timezone=False), nullable=False) """not_before (datetime): Certificate validity - not before""" not_after = db.Column(db.DateTime(timezone=False), nullable=False) """not_after (datetime): Certificate validity - not after""" serial = db.Column(db.BigInteger) """serial (int): Serial Number""" # SHA-256 hash of DER-encoded certificate fingerprint = db.Column(db.String(64), nullable=False, index=True, unique=True) # Unique """fingerprint (str): SHA-256 hash of certificate""" push_topic = db.Column(db.String, nullable=True) # Only required for push certificate """push_topic (str): Only present for Push Certificates, the x.509 User ID field value""" discriminator = db.Column(db.String(20)) """discriminator (str): The type of certificate""" __mapper_args__ = { 'polymorphic_on': discriminator, 'polymorphic_identity': 'certificates', } @classmethod def from_crypto_type(cls, certificate: x509.Certificate, certtype: CertificateType): # type: (certtype, x509.Certificate, CertificateType) -> Certificate m = cls() m.pem_data = certificate.public_bytes(serialization.Encoding.PEM) m.not_after = certificate.not_valid_after m.not_before = certificate.not_valid_before m.fingerprint = certificate.fingerprint(hashes.SHA256()) m.discriminator = certtype.value m.serial = certificate.serial_number subject: x509.Name = certificate.subject cns = subject.get_attributes_for_oid(NameOID.COMMON_NAME) if cns is not None: m.x509_cn = cns[0].value return m
class CertificateAuthority(db.Model): """Certificate authority storage: database implementation. I'm loathe to create a model tied to the storage implementation but this was the easiest option at the time. """ __tablename__ = 'certificate_authority' id = db.Column(db.Integer, primary_key=True) common_name = db.Column(db.String, unique=True) serial = db.Column(db.Integer, default=0) validity_period = db.Column(db.Integer, default=365) certificate_id = db.Column(db.Integer, db.ForeignKey('certificates.id')) certificate = db.relationship('CACertificate', backref='certificate_authority') rsa_private_key_id = db.Column(db.Integer, db.ForeignKey('rsa_private_keys.id')) rsa_private_key = db.relationship('RSAPrivateKey', backref='certificate_authority') @classmethod def create(cls, common_name: str = 'COMMANDMENT-CA', key_size=2048): ca = cls() ca.common_name = common_name name = x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, common_name), x509.NameAttribute(NameOID.ORGANIZATION_NAME, 'commandment') ]) private_key = rsa.generate_private_key( public_exponent=65537, key_size=key_size, backend=default_backend(), ) ca.rsa_private_key = RSAPrivateKey.from_crypto(private_key) db.session.add(ca.rsa_private_key) certificate = x509.CertificateBuilder().subject_name(name).issuer_name( name).public_key(private_key.public_key()).serial_number( x509.random_serial_number()).not_valid_before( datetime.datetime.utcnow()).not_valid_after( datetime.datetime.utcnow() + datetime.timedelta(days=365)).add_extension( x509.BasicConstraints(ca=True, path_length=None), True).sign(private_key, hashes.SHA256(), default_backend()) ca_certificate_model = CACertificate.from_crypto(certificate) ca_certificate_model.rsa_private_key = ca.rsa_private_key ca.certificate = ca_certificate_model db.session.add(ca) db.session.commit() return ca def create_device_csr( self, common_name: str ) -> (rsa.RSAPrivateKeyWithSerialization, x509.CertificateSigningRequest): """ Create a Certificate Signing Request with the specified Common Name. The private key model is automatically committed to the database. This is also true for the certificate signing request. Args: common_name (str): The certificate Common Name attribute Returns: Tuple[rsa.RSAPrivateKeyWithSerialization, x509.CertificateSigningRequest] - A tuple containing the RSA Private key that was generated, along with the CSR. """ private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend(), ) private_key_model = RSAPrivateKey.from_crypto(private_key) db.session.add(private_key_model) db.session.commit() name = x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, common_name), x509.NameAttribute(NameOID.ORGANIZATION_NAME, 'commandment') ]) builder = x509.CertificateSigningRequestBuilder() builder = builder.subject_name(name) builder = builder.add_extension(x509.BasicConstraints( ca=False, path_length=None), critical=True) request = builder.sign(private_key, hashes.SHA256(), default_backend()) csr_model = CertificateSigningRequest().from_crypto(request) db.session.add(csr_model) db.session.commit() return private_key, request def sign(self, request: x509.CertificateSigningRequest) -> x509.Certificate: """ Sign a Certificate Signing Request. The issued certificate is automatically persisted to the database. Args: request (x509.CertificateSigningRequest): The CSR object (cryptography) not the SQLAlchemy model. Returns: x509.Certificate: A signed certificate """ b = x509.CertificateBuilder() self.serial += 1 private_key_model = self.rsa_private_key private_key = private_key_model.to_crypto() # ca_certificate_model = self.certificate # ca_certificate = ca_certificate_model.to_crypto() name = x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, self.common_name), x509.NameAttribute(NameOID.ORGANIZATION_NAME, 'commandment') ]) cert = b.not_valid_before(datetime.datetime.utcnow()).not_valid_after( datetime.datetime.utcnow() + datetime.timedelta(days=self.validity_period)).serial_number( self.serial).issuer_name(name).subject_name( request.subject).public_key(request.public_key()).sign( private_key, hashes.SHA256(), default_backend()) # cert_model = DeviceIdentityCertificate().from_crypto(cert) # db.session.add(cert_model) # db.session.commit() return cert
class DEPProfile(db.Model): __tablename__ = 'dep_profiles' id = db.Column(db.Integer, primary_key=True) uuid = db.Column(GUID, index=True) profile_name = db.Column(db.String, nullable=False) url = db.Column(db.String, nullable=False) allow_pairing = db.Column(db.Boolean) is_supervised = db.Column(db.Boolean) is_multi_user = db.Column(db.Boolean) is_mandatory = db.Column(db.Boolean) await_device_configured = db.Column(db.Boolean) is_mdm_removable = db.Column(db.Boolean) support_phone_number = db.Column(db.String) auto_advance_setup = db.Column(db.Boolean) support_email_address = db.Column(db.String) org_magic = db.Column(db.String) # skip_setup_items = db.Column(db.Enum(SkipSetupSteps)) department = db.Column(db.String) anchor_certs = db.relationship( 'DEPAnchorCertificate', secondary=dep_profile_anchor_certificates, # back_populates='anchor_dep_profiles' ) supervising_host_certs = db.relationship( 'DEPSupervisionCertificate', secondary=dep_profile_supervision_certificates, # back_populates='supervising_dep_profiles' )
class DEPAccount(db.Model): """DEP Account This table stores information about a single DEP account (aka one 'MDM Server' in the portal), and its current token. """ __tablename__ = 'dep_accounts' id = db.Column(db.Integer, primary_key=True) # certificate for PKI of server token certificate_id = db.Column(db.ForeignKey('certificates.id')) certificate = db.relationship('DEPServerTokenCertificate', backref='dep_configurations') # OAuth creds consumer_key = db.Column(db.String()) consumer_secret = db.Column(db.String()) access_token = db.Column(db.String()) access_secret = db.Column(db.String()) access_token_expiry = db.Column(db.DateTime()) token_updated_at = db.Column(db.DateTime()) # Current session token auth_session_token = db.Column(db.String()) # Information synchronised from the /account endpoint server_name = db.Column(db.String()) server_uuid = db.Column(GUID) admin_id = db.Column(db.String()) facilitator_id = db.Column(db.String()) org_name = db.Column(db.String()) org_email = db.Column(db.String()) org_phone = db.Column(db.String()) org_address = db.Column(db.String()) org_type = db.Column(db.Enum(DEPOrgType)) org_version = db.Column(db.Enum(DEPOrgVersion)) org_id = db.Column(db.String()) org_id_hash = db.Column(db.String()) url = db.Column(db.String()) # Hold the state of the in-progress fetch/sync in case the DEP thread dies cursor = db.Column(db.String()) more_to_follow = db.Column(db.Boolean()) fetched_until = db.Column(db.DateTime())
org_version = db.Column(db.Enum(DEPOrgVersion)) org_id = db.Column(db.String()) org_id_hash = db.Column(db.String()) url = db.Column(db.String()) # Hold the state of the in-progress fetch/sync in case the DEP thread dies cursor = db.Column(db.String()) more_to_follow = db.Column(db.Boolean()) fetched_until = db.Column(db.DateTime()) dep_profile_anchor_certificates = db.Table( 'dep_profile_anchor_certificates', db.metadata, db.Column('dep_profile_id', db.Integer, db.ForeignKey('dep_profiles.id')), db.Column('certificate_id', db.Integer, db.ForeignKey('certificates.id')), ) dep_profile_supervision_certificates = db.Table( 'dep_profile_supervision_certificates', db.metadata, db.Column('dep_profile_id', db.Integer, db.ForeignKey('dep_profiles.id')), db.Column('certificate_id', db.Integer, db.ForeignKey('certificates.id')), ) class DEPProfile(db.Model): __tablename__ = 'dep_profiles' id = db.Column(db.Integer, primary_key=True) uuid = db.Column(GUID, index=True)
class InstalledProfile(db.Model): """This model represents a single installed profile on an enrolled device as returned by the ``ProfileList`` query. The response does not contain the entire contents of the profiles installed therefore the UUIDs returned are joined against our profiles table to ascertain whether profiles have been installed or not. :table: installed_profiles See Also: - `ProfileList Command <https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/MobileDeviceManagementProtocolRef/3-MDM_Protocol/MDM_Protocol.html#//apple_ref/doc/uid/TP40017387-CH3-SW7>`_. """ __tablename__ = 'installed_profiles' id = db.Column(db.Integer, primary_key=True) """(int): Installed Profile ID""" device_udid = db.Column(db.String(40), index=True, nullable=False) """(GUID): Unique Device Identifier""" device_id = db.Column(db.ForeignKey('devices.id'), nullable=True) """(int): Device foreign key ID.""" device = db.relationship('Device', backref='installed_profiles') """(db.relationship): Device relationship""" has_removal_password = db.Column(db.Boolean) """(bool): Does the installed profile have a removal password?""" is_encrypted = db.Column(db.Boolean) """(bool): Is the installed profile encrypted?""" payload_description = db.Column(db.String) """(str): Payload description (value of PayloadDescription)""" payload_display_name = db.Column(db.String) """(str): Payload display name""" payload_identifier = db.Column(db.String) payload_organization = db.Column(db.String) payload_removal_disallowed = db.Column(db.Boolean) payload_uuid = db.Column(GUID, index=True)
class AvailableOSUpdate(db.Model): """This table holds the results of `AvailableOSUpdates` commands.""" __tablename__ = 'available_os_updates' id = db.Column(db.Integer, primary_key=True) device_id = db.Column(db.ForeignKey('devices.id'), nullable=True) """(int): Device foreign key ID.""" device = db.relationship('Device', backref='available_os_updates') """(db.relationship): Device relationship""" # Common to all platforms allows_install_later = db.Column(db.Boolean) human_readable_name = db.Column(db.String) is_critical = db.Column(db.Boolean) product_key = db.Column(db.String) restart_required = db.Column(db.Boolean) version = db.Column(db.String) # macOS Only app_identifiers_to_close = db.Column( MutableList.as_mutable(JSONEncodedDict)) human_readable_name_locale = db.Column(db.String) is_config_data_update = db.Column(db.Boolean) """(bool): This update is a config data update eg. for XProtect or Gatekeeper. These arent normally shown""" is_firmware_update = db.Column(db.Boolean) metadata_url = db.Column(db.String) # iOS Only product_name = db.Column(db.String) build = db.Column(db.String) download_size = db.Column(db.BigInteger) install_size = db.Column(db.BigInteger)
class DEPProfile(db.Model): __tablename__ = 'dep_profiles' id = db.Column(db.Integer, primary_key=True) uuid = db.Column(GUID, index=True) # A profile is defined under a single DEP account dep_account_id = db.Column(db.Integer, db.ForeignKey('dep_accounts.id')) dep_account = db.relationship('DEPAccount', backref='dep_profiles', foreign_keys=[dep_account_id]) profile_name = db.Column(db.String, nullable=False) url = db.Column(db.String, nullable=False) allow_pairing = db.Column(db.Boolean, default=True) is_supervised = db.Column(db.Boolean, default=False) is_multi_user = db.Column(db.Boolean, default=False) is_mandatory = db.Column(db.Boolean, default=False) await_device_configured = db.Column(db.Boolean, default=False) is_mdm_removable = db.Column(db.Boolean, default=True) support_phone_number = db.Column(db.String) auto_advance_setup = db.Column(db.Boolean, default=False) support_email_address = db.Column(db.String) org_magic = db.Column(db.String) skip_setup_items = db.Column(SetOfEnumValues(SetupAssistantStep)) department = db.Column(db.String) # language = db.Column(db.String) # region = db.Column(db.String) # last_upload_at = db.Column(db.DateTime) anchor_certs = db.relationship( 'DEPAnchorCertificate', secondary=dep_profile_anchor_certificates, # back_populates='anchor_dep_profiles' ) supervising_host_certs = db.relationship( 'DEPSupervisionCertificate', secondary=dep_profile_supervision_certificates, # back_populates='supervising_dep_profiles' )