Beispiel #1
0
    def setUp(self):
        self.profile = "facilitydata"

        self.root_scope_def = ScopeDefinition.objects.create(
            id="rootcert",
            profile=self.profile,
            version=1,
            primary_scope_param_key="mainpartition",
            description="Root cert for ${mainpartition}.",
            read_filter_template="",
            write_filter_template="",
            read_write_filter_template="${mainpartition}",
        )

        self.subset_scope_def = ScopeDefinition.objects.create(
            id="subcert",
            profile=self.profile,
            version=1,
            primary_scope_param_key="",
            description=
            "Subset cert under ${mainpartition} for ${subpartition}.",
            read_filter_template="${mainpartition}",
            write_filter_template="${mainpartition}:${subpartition}",
            read_write_filter_template="",
        )

        self.root_cert = Certificate.generate_root_certificate(
            self.root_scope_def.id)

        self.subset_cert = Certificate(
            parent=self.root_cert,
            profile=self.profile,
            scope_definition=self.subset_scope_def,
            scope_version=self.subset_scope_def.version,
            scope_params=json.dumps({
                "mainpartition": self.root_cert.id,
                "subpartition": "abracadabra"
            }),
            private_key=Key(),
        )
        self.root_cert.sign_certificate(self.subset_cert)
        self.subset_cert.save()

        self.unsaved_cert = Certificate(
            parent=self.root_cert,
            profile=self.profile,
            scope_definition=self.subset_scope_def,
            scope_version=self.subset_scope_def.version,
            scope_params=json.dumps({
                "mainpartition": self.root_cert.id,
                "subpartition": "other"
            }),
            public_key=Key(),
        )
        self.root_cert.sign_certificate(self.unsaved_cert)

        self.controller = MorangoProfileController("facilitydata")
        self.network_connection = self.controller.create_network_connection(
            self.live_server_url)
        self.key = SharedKey.get_or_create_shared_key()
Beispiel #2
0
class CertificateTestCaseMixin(object):
    def setUp(self):

        self.profile = "testprofile"

        self.root_scope_def = ScopeDefinition.objects.create(
            id="rootcert",
            profile=self.profile,
            version=1,
            primary_scope_param_key="mainpartition",
            description="Root cert for ${mainpartition}.",
            read_filter_template="",
            write_filter_template="",
            read_write_filter_template="${mainpartition}",
        )

        self.subset_scope_def = ScopeDefinition.objects.create(
            id="subcert",
            profile=self.profile,
            version=1,
            primary_scope_param_key="",
            description=
            "Subset cert under ${mainpartition} for ${subpartition}.",
            read_filter_template="${mainpartition}",
            write_filter_template="${mainpartition}:${subpartition}",
            read_write_filter_template="",
        )

        self.root_cert = Certificate.generate_root_certificate(
            self.root_scope_def.id)

        self.subset_cert = Certificate(
            parent=self.root_cert,
            profile=self.profile,
            scope_definition=self.subset_scope_def,
            scope_version=self.subset_scope_def.version,
            scope_params=json.dumps({
                "mainpartition": self.root_cert.id,
                "subpartition": "abracadabra"
            }),
            private_key=Key(),
        )
        self.root_cert.sign_certificate(self.subset_cert)
        self.subset_cert.save()
Beispiel #3
0
    def certificate_signing_request(
        self,
        parent_cert,
        scope_definition_id,
        scope_params,
        userargs=None,
        password=None,
    ):
        # if server cert does not exist locally, retrieve it from server
        if not Certificate.objects.filter(id=parent_cert.id).exists():
            cert_chain_response = self._get_certificate_chain(
                params={"ancestors_of": parent_cert.id})

            # upon receiving cert chain from server, we attempt to save the chain into our records
            Certificate.save_certificate_chain(cert_chain_response.json(),
                                               expected_last_id=parent_cert.id)

        csr_key = Key()
        # build up data for csr
        data = {
            "parent": parent_cert.id,
            "profile": parent_cert.profile,
            "scope_definition": scope_definition_id,
            "scope_version": parent_cert.scope_version,
            "scope_params": json.dumps(scope_params),
            "public_key": csr_key.get_public_key_string(),
        }
        csr_resp = self._certificate_signing(data, userargs, password)
        csr_data = csr_resp.json()

        # verify cert returned from server, and proceed to save into our records
        csr_cert = Certificate.deserialize(csr_data["serialized"],
                                           csr_data["signature"])
        csr_cert.private_key = csr_key
        csr_cert.check_certificate()
        csr_cert.save()
        return csr_cert
Beispiel #4
0
    def get_remote_certificates(self, primary_partition, scope_def_id=None):
        remote_certs = []
        # request certs for this primary partition, where the server also has a private key for
        remote_certs_resp = self._get_certificate_chain(
            params={"primary_partition": primary_partition})

        # inflate remote certs into a list of unsaved models
        for cert in remote_certs_resp.json():
            remote_certs.append(
                Certificate.deserialize(cert["serialized"], cert["signature"]))

        # filter certs by scope definition id, if provided
        if scope_def_id:
            remote_certs = [
                cert for cert in remote_certs
                if cert.scope_definition_id == scope_def_id
            ]

        return remote_certs
Beispiel #5
0
    def test_bad_scope_subset_does_not_validate(self):

        bad_subset_cert = Certificate(
            parent=self.root_cert,
            profile=self.profile,
            scope_definition=self.subset_scope_def,
            scope_version=self.subset_scope_def.version,
            scope_params=json.dumps({
                "mainpartition": "a" * 32,
                "subpartition": "abracadabra"
            }),
            private_key=Key(),
        )
        self.root_cert.sign_certificate(bad_subset_cert)
        bad_subset_cert.save()

        with self.assertRaises(CertificateScopeNotSubset):
            bad_subset_cert.check_certificate()
Beispiel #6
0
    def push_signed_client_certificate_chain(self, local_parent_cert,
                                             scope_definition_id,
                                             scope_params):
        if ALLOW_CERTIFICATE_PUSHING not in self.capabilities:
            raise MorangoServerDoesNotAllowNewCertPush(
                "Server does not allow certificate pushing")

        # grab shared public key of server
        publickey_response = self._get_public_key()

        # request the server for a one-time-use nonce
        nonce_response = self._get_nonce()

        # build up data for csr
        certificate = Certificate(
            parent_id=local_parent_cert.id,
            profile=local_parent_cert.profile,
            scope_definition_id=scope_definition_id,
            scope_version=local_parent_cert.scope_version,
            scope_params=json.dumps(scope_params),
            public_key=Key(
                public_key_string=publickey_response.json()[0]["public_key"]),
            salt=nonce_response.json()
            ["id"],  # for pushing signed certs, we use nonce as salt
        )

        # add ID and signature to the certificate
        certificate.id = certificate.calculate_uuid()
        certificate.parent.sign_certificate(certificate)

        # serialize the chain for sending to server
        certificate_chain = list(
            local_parent_cert.get_ancestors(
                include_self=True)) + [certificate]
        data = json.dumps(
            CertificateSerializer(certificate_chain, many=True).data)

        # client sends signed certificate chain to server
        self._push_certificate_chain(data)

        # if there are no errors, we can save the pushed certificate
        certificate.save()
        return certificate
Beispiel #7
0
 def test_setting_public_key_does_not_set_private_key(self):
     cert = Certificate()
     cert.public_key = Key()
     self.assertEqual(cert.private_key, None)
Beispiel #8
0
 def test_setting_private_key_sets_public_key(self):
     cert = Certificate()
     cert.private_key = Key()
     self.assertTrue(
         cert.public_key.verify("testval",
                                cert.private_key.sign("testval")))
Beispiel #9
0
 def setUp(self):
     super(CertificateSerializationTestCase, self).setUp()
     self.root_cert_deserialized = Certificate.deserialize(
         self.root_cert.serialized, self.root_cert.signature)
     self.subset_cert_deserialized = Certificate.deserialize(
         self.subset_cert.serialized, self.subset_cert.signature)
Beispiel #10
0
    def create_sync_session(self, client_cert, server_cert, chunk_size=500):
        # if server cert does not exist locally, retrieve it from server
        if not Certificate.objects.filter(id=server_cert.id).exists():
            cert_chain_response = self._get_certificate_chain(
                params={"ancestors_of": server_cert.id})

            # upon receiving cert chain from server, we attempt to save the chain into our records
            Certificate.save_certificate_chain(cert_chain_response.json(),
                                               expected_last_id=server_cert.id)

        # request the server for a one-time-use nonce
        nonce_resp = self._get_nonce()
        nonce = nonce_resp.json()["id"]

        # if no hostname then url is actually an ip
        url = urlparse(self.base_url)
        hostname = url.hostname or self.base_url
        port = url.port or (80 if url.scheme == "http" else 443)

        # prepare the data to send in the syncsession creation request
        data = {
            "id":
            uuid.uuid4().hex,
            "server_certificate_id":
            server_cert.id,
            "client_certificate_id":
            client_cert.id,
            "profile":
            client_cert.profile,
            "certificate_chain":
            json.dumps(
                CertificateSerializer(
                    client_cert.get_ancestors(include_self=True),
                    many=True).data),
            "connection_path":
            self.base_url,
            "instance":
            json.dumps(
                InstanceIDSerializer(
                    InstanceIDModel.get_or_create_current_instance()[0]).data),
            "nonce":
            nonce,
            "client_ip":
            _get_client_ip_for_server(hostname, port),
            "server_ip":
            _get_server_ip(hostname),
        }

        # sign the nonce/ID combo to attach to the request
        message = "{nonce}:{id}".format(**data)
        data["signature"] = client_cert.sign(message)

        # Sync Session creation request
        session_resp = self._create_sync_session(data)

        # check that the nonce/id were properly signed by the server cert
        if not server_cert.verify(message,
                                  session_resp.json().get("signature")):
            raise CertificateSignatureInvalid()

        # build the data to be used for creating our own syncsession
        data = {
            "id":
            data["id"],
            "start_timestamp":
            timezone.now(),
            "last_activity_timestamp":
            timezone.now(),
            "active":
            True,
            "is_server":
            False,
            "client_certificate":
            client_cert,
            "server_certificate":
            server_cert,
            "profile":
            client_cert.profile,
            "connection_kind":
            "network",
            "connection_path":
            self.base_url,
            "client_ip":
            data["client_ip"],
            "server_ip":
            data["server_ip"],
            "client_instance":
            json.dumps(
                InstanceIDSerializer(
                    InstanceIDModel.get_or_create_current_instance()[0]).data),
            "server_instance":
            session_resp.json().get("server_instance") or "{}",
        }
        sync_session = SyncSession.objects.create(**data)
        return SyncSessionClient(self, sync_session, chunk_size=chunk_size)
Beispiel #11
0
class NetworkSyncConnectionTestCase(LiveServerTestCase):
    def setUp(self):
        self.profile = "facilitydata"

        self.root_scope_def = ScopeDefinition.objects.create(
            id="rootcert",
            profile=self.profile,
            version=1,
            primary_scope_param_key="mainpartition",
            description="Root cert for ${mainpartition}.",
            read_filter_template="",
            write_filter_template="",
            read_write_filter_template="${mainpartition}",
        )

        self.subset_scope_def = ScopeDefinition.objects.create(
            id="subcert",
            profile=self.profile,
            version=1,
            primary_scope_param_key="",
            description=
            "Subset cert under ${mainpartition} for ${subpartition}.",
            read_filter_template="${mainpartition}",
            write_filter_template="${mainpartition}:${subpartition}",
            read_write_filter_template="",
        )

        self.root_cert = Certificate.generate_root_certificate(
            self.root_scope_def.id)

        self.subset_cert = Certificate(
            parent=self.root_cert,
            profile=self.profile,
            scope_definition=self.subset_scope_def,
            scope_version=self.subset_scope_def.version,
            scope_params=json.dumps({
                "mainpartition": self.root_cert.id,
                "subpartition": "abracadabra"
            }),
            private_key=Key(),
        )
        self.root_cert.sign_certificate(self.subset_cert)
        self.subset_cert.save()

        self.unsaved_cert = Certificate(
            parent=self.root_cert,
            profile=self.profile,
            scope_definition=self.subset_scope_def,
            scope_version=self.subset_scope_def.version,
            scope_params=json.dumps({
                "mainpartition": self.root_cert.id,
                "subpartition": "other"
            }),
            public_key=Key(),
        )
        self.root_cert.sign_certificate(self.unsaved_cert)

        self.controller = MorangoProfileController("facilitydata")
        self.network_connection = self.controller.create_network_connection(
            self.live_server_url)
        self.key = SharedKey.get_or_create_shared_key()

    @mock.patch.object(SyncSession.objects, "create", return_value=None)
    def test_creating_sync_session_successful(self, mock_object):
        self.assertEqual(SyncSession.objects.filter(active=True).count(), 0)
        self.network_connection.create_sync_session(self.subset_cert,
                                                    self.root_cert)
        self.assertEqual(SyncSession.objects.filter(active=True).count(), 1)

    @mock.patch.object(NetworkSyncConnection, "_create_sync_session")
    @mock.patch.object(Certificate, "verify", return_value=False)
    def test_creating_sync_session_cert_fails_to_verify(
            self, mock_verify, mock_create):
        mock_create.return_value.json.return_value = {}
        with self.assertRaises(CertificateSignatureInvalid):
            self.network_connection.create_sync_session(
                self.subset_cert, self.root_cert)

    def test_get_remote_certs(self):
        certs = self.subset_cert.get_ancestors(include_self=True)
        remote_certs = self.network_connection.get_remote_certificates(
            self.root_cert.id)
        self.assertSetEqual(set(certs), set(remote_certs))

    @mock.patch.object(SessionWrapper, "request")
    def test_csr(self, mock_request):
        # mock a "signed" cert being returned by server
        cert_serialized = json.dumps(
            CertificateSerializer(self.subset_cert).data)
        mock_request.return_value.json.return_value = json.loads(
            cert_serialized)
        self.subset_cert.delete()

        # we only want to make sure the "signed" cert is saved
        with mock.patch.object(
                Key,
                "get_private_key_string",
                return_value=self.subset_cert.private_key.
                get_private_key_string(),
        ):
            self.network_connection.certificate_signing_request(
                self.root_cert, "", "")
        self.assertTrue(
            Certificate.objects.filter(
                id=json.loads(cert_serialized)["id"]).exists())

    @override_settings(ALLOW_CERTIFICATE_PUSHING=True)
    def test_push_signed_client_certificate_chain(self):
        self.network_connection.capabilities = [ALLOW_CERTIFICATE_PUSHING]
        cert = self.network_connection.push_signed_client_certificate_chain(
            self.root_cert,
            self.subset_scope_def.id,
            {
                "mainpartition": self.root_cert.id,
                "subpartition": "abracadabra"
            },
        )
        self.assertEqual(cert.private_key, None)
        self.assertTrue(Certificate.objects.filter(id=cert.id).exists())

    @override_settings(ALLOW_CERTIFICATE_PUSHING=True)
    def test_push_signed_client_certificate_chain_publickey_error(self):
        self.network_connection.capabilities = [ALLOW_CERTIFICATE_PUSHING]
        with mock.patch.object(NetworkSyncConnection, "_get_public_key"):
            NetworkSyncConnection._get_public_key.return_value.json.return_value = [
                {
                    "public_key": Key().get_public_key_string()
                }
            ]
            with self.assertRaises(HTTPError) as e:
                self.network_connection.push_signed_client_certificate_chain(
                    self.root_cert,
                    self.subset_scope_def.id,
                    {
                        "mainpartition": self.root_cert.id,
                        "subpartition": "abracadabra"
                    },
                )
            self.assertEqual(e.exception.response.status_code, 400)

    @override_settings(ALLOW_CERTIFICATE_PUSHING=True)
    def test_push_signed_client_certificate_chain_bad_cert(self):
        self.network_connection.capabilities = [ALLOW_CERTIFICATE_PUSHING]
        with self.assertRaises(HTTPError) as e:
            self.network_connection.push_signed_client_certificate_chain(
                self.root_cert, self.subset_scope_def.id,
                {"bad": "scope_params"})
        self.assertEqual(e.exception.response.status_code, 400)

    @override_settings(ALLOW_CERTIFICATE_PUSHING=True)
    @mock.patch.object(NetworkSyncConnection, "_get_nonce")
    def test_push_signed_client_certificate_chain_nonce_error(
            self, mock_nonce):
        self.network_connection.capabilities = [ALLOW_CERTIFICATE_PUSHING]
        mock_nonce.return_value.json.return_value = {"id": uuid.uuid4().hex}
        with self.assertRaises(HTTPError) as e:
            self.network_connection.push_signed_client_certificate_chain(
                self.root_cert,
                self.subset_scope_def.id,
                {
                    "mainpartition": self.root_cert.id,
                    "subpartition": "abracadabra"
                },
            )
        self.assertEqual(e.exception.response.status_code, 403)

    def test_push_signed_client_certificate_chain_not_allowed(self):
        with self.assertRaises(MorangoServerDoesNotAllowNewCertPush) as e:
            self.network_connection.push_signed_client_certificate_chain(
                self.root_cert,
                self.subset_scope_def.id,
                {
                    "mainpartition": self.root_cert.id,
                    "subpartition": "abracadabra"
                },
            )
            self.assertEqual(e.exception.response.status_code, 403)

    def test_get_cert_chain(self):
        response = self.network_connection._get_certificate_chain(
            params={"ancestors_of": self.subset_cert.id})
        data = response.json()
        self.assertEqual(len(data), Certificate.objects.count())
        self.assertEqual(data[0]["id"], self.root_cert.id)
        self.assertEqual(data[1]["id"], self.subset_cert.id)

    @mock.patch.object(SyncSession.objects, "create")
    def test_close_sync_session(self, mock_create):
        mock_session = mock.Mock(spec=SyncSession)

        def create(**data):
            mock_session.id = data.get("id")
            return mock_session

        mock_create.side_effect = create
        self.assertEqual(SyncSession.objects.filter(active=True).count(), 0)
        client = self.network_connection.create_sync_session(
            self.subset_cert, self.root_cert)
        self.assertEqual(SyncSession.objects.filter(active=True).count(), 1)

        self.network_connection.close_sync_session(client.sync_session)
        self.assertEqual(SyncSession.objects.filter(active=True).count(), 0)