예제 #1
0
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)
예제 #2
0
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
예제 #3
0
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')
예제 #4
0
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')
예제 #5
0
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
예제 #6
0
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()
예제 #7
0
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."""
예제 #8
0
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())
예제 #9
0
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)
예제 #10
0
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
예제 #11
0
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
예제 #12
0
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'
    )
예제 #13
0
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())
예제 #14
0
    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)
예제 #15
0
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)
예제 #16
0
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)
예제 #17
0
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'
    )