Exemple #1
0
    async def download_secret(self, member_id: UUID = None):

        if not member_id:
            member_id = self.member_id
        elif isinstance(member_id, str):
            member_id = UUID(member_id)

        fqdn = MemberSecret.create_commonname(
            member_id, self.service_id, self.network.name
        )
        response = await ApiClient.call(
            f'https://{fqdn}/member-cert.pem'
        )

        if response.status != 200:
            raise RuntimeError(
                'Download the member cert resulted in status: '
                f'{response.status}'
            )

        certchain = await response.text()

        secret = MemberSecret(member_id, self.service_id, self.account)
        secret.from_string(certchain)

        return secret
Exemple #2
0
    async def create_secrets(self, members_ca: MembersCaSecret = None) -> None:
        '''
        Creates the secrets for a membership
        '''

        if self.tls_secret and await self.tls_secret.cert_file_exists():
            self.tls_secret = MemberSecret(
                None, self.service_id, self.account
            )
            await self.tls_secret.load(
                with_private_key=True, password=self.private_key_password
            )
            self.member_id = self.tls_secret.member_id
        else:
            self.tls_secret = await self._create_secret(
                MemberSecret, members_ca
            )

        if self.data_secret and await self.data_secret.cert_file_exists():
            self.data_secret = MemberDataSecret(
                self.member_id, self.service_id, self.account
            )
            await self.data_secret.load(
                with_private_key=True, password=self.private_key_password

            )
        else:
            self.data_secret = await self._create_secret(
                MemberDataSecret, members_ca
            )
    def test_member_putpost(self):
        API = BASE_URL + '/v1/service/member'

        service = config.server.service

        member_id = uuid4()

        # HACK: MemberSecret takes an Account instance as third parameter but
        # we use a Service instance instead
        service.paths.account = 'pod'
        secret = MemberSecret(member_id, SERVICE_ID, service)
        csr = secret.create_csr()
        csr = csr.public_bytes(serialization.Encoding.PEM)

        response = requests.post(API,
                                 json={'csr': str(csr, 'utf-8')},
                                 headers=None)
        self.assertEqual(response.status_code, 201)
        data = response.json()

        self.assertTrue('signed_cert' in data)
        self.assertTrue('cert_chain' in data)
        self.assertTrue('service_data_cert_chain' in data)

        signed_secret = MemberSecret(member_id, SERVICE_ID, service)
        signed_secret.from_string(data['signed_cert'],
                                  certchain=data['cert_chain'])

        service_data_cert_chain = secret.from_string(  # noqa: F841
            data['service_data_cert_chain'])

        membersecret_commonname = Secret.extract_commonname(signed_secret.cert)
        memberscasecret_commonname = Secret.extract_commonname(
            signed_secret.cert_chain[0])

        # PUT, with auth
        # In the PUT body we put the member data secret as a service may
        # have use for it in the future.
        member_data_secret = MemberDataSecret(member_id, SERVICE_ID, service)
        csr = member_data_secret.create_csr()
        cert_chain = service.members_ca.sign_csr(csr)
        member_data_secret.from_signed_cert(cert_chain)
        member_data_certchain = member_data_secret.certchain_as_pem()

        headers = {
            'X-Client-SSL-Verify': 'SUCCESS',
            'X-Client-SSL-Subject': f'CN={membersecret_commonname}',
            'X-Client-SSL-Issuing-CA': f'CN={memberscasecret_commonname}'
        }
        response = requests.put(f'{API}/version/1',
                                headers=headers,
                                json={'certchain': member_data_certchain})
        self.assertEqual(response.status_code, 200)
        data = response.json()
        self.assertEqual(data['ipv4_address'], '127.0.0.1')
        self.assertEqual(data['ipv6_address'], None)
Exemple #4
0
    async def load_secrets(self) -> None:
        '''
        Loads the membership secrets
        '''

        self.tls_secret = MemberSecret(
            None, self.service_id, self.account
        )
        await self.tls_secret.load(
            with_private_key=True, password=self.private_key_password
        )
        self.member_id = self.tls_secret.member_id

        self.data_secret = MemberDataSecret(
            self.member_id, self.service_id, self.account
        )
        await self.data_secret.load(
            with_private_key=True, password=self.private_key_password
        )
Exemple #5
0
    def compose_fqdn(self, uuid: UUID, id_type: IdType,
                     service_id: Optional[int] = None) -> str:
        '''
        Generate the FQDN for an id of the specified type

        :param uuid: identifier for the account or member. Must be None for
            IdType.SERVICE
        :param id_type: type of service
        :param service_id: identifier for the service, required for
            IdType.MEMBER and IdType.ACCOUNT
        :returns: FQDN
        :raises: (none)
        '''

        self._validate_parameters(uuid, id_type, service_id=service_id)

        if id_type == IdType.MEMBER:
            return MemberSecret.create_commonname(
                uuid, service_id, self.domain
            )
        elif id_type == IdType.ACCOUNT:
            return AccountSecret.create_commonname(uuid, self.domain)
        elif id_type == IdType.SERVICE:
            return ServiceSecret.create_commonname(service_id, self.domain)
    async def asyncSetUp(self):
        try:
            shutil.rmtree(TEST_DIR)
        except FileNotFoundError:
            pass

        os.makedirs(TEST_DIR)
        shutil.copyfile(DEFAULT_SCHEMA,
                        TEST_DIR + '/' + os.path.split(DEFAULT_SCHEMA)[-1])
        network = await Network.create(NETWORK, TEST_DIR, 'byoda')

        # Remaining environment variables used:
        config.server = PodServer(network)
        server = config.server

        global BASE_URL
        BASE_URL = BASE_URL.format(PORT=server.HTTP_PORT)

        await server.set_document_store(DocumentStoreType.OBJECT_STORE,
                                        cloud_type=CloudType.LOCAL,
                                        bucket_prefix='byodatest',
                                        root_dir=TEST_DIR)

        server.paths = network.paths

        account_id = uuid4()
        pod_account = Account(account_id, network)
        await pod_account.paths.create_account_directory()
        await pod_account.load_memberships()

        server.account = pod_account

        # We can't join the service as it doesn't exist in the network
        # so we have to use our own membership logic
        service = Service(network=network,
                          service_id=SERVICE_ID,
                          storage_driver=network.paths.storage_driver)
        service.name = 'jsonschema_test'
        await service.create_secrets(network.services_ca)

        member = Member(SERVICE_ID, pod_account)
        await member.setup()

        member.member_id = UUID(MEMBER_ID)
        pod_account.memberships[SERVICE_ID] = member

        member.tls_secret = MemberSecret(member.member_id, member.service_id,
                                         member.account)
        member.data_secret = MemberDataSecret(member.member_id,
                                              member.service_id,
                                              member.account)

        await member.create_secrets(members_ca=service.members_ca)

        member.data_secret.create_shared_key()

        member.schema = await member.load_schema(
            os.path.split(DEFAULT_SCHEMA)[-1], verify_signatures=False)

        member.data = MemberData(member, member.paths, member.document_store)
        await member.data.save_protected_shared_key()
        member.data.initalize()
        await member.data.save()

        app = Starlette(debug=True)
        graphql_app = GraphQL(member.schema.gql_schema, debug=True)
        for path in ['/', '/graphql']:
            app.add_route(path, graphql_app)

        TestJsonSchema.PROCESS = Process(target=uvicorn.run,
                                         args=(app, ),
                                         kwargs={
                                             'host': '0.0.0.0',
                                             'port': server.HTTP_PORT,
                                             'log_level': 'info'
                                         },
                                         daemon=True)
        TestJsonSchema.PROCESS.start()
        await asyncio.sleep(3)
Exemple #7
0
    async def create(service: Service, schema_version: int,
                     account: Account, member_id: UUID = None,
                     members_ca: MembersCaSecret = None,
                     local_service_contract: str = None):
        '''
        Factory for a new membership

        :param service: the service to become a member from
        :param schema_version: the version of the service contract to use
        :param account: the account becoming a member
        :param member_id: the memebr ID
        :param local_service_contract: The service contract to sideload from
        the local file system. This parameter must only be used by test cases
        '''

        if local_service_contract and not config.test_case:
            raise ValueError(
                'storage_driver and filepath parameters can only be used by '
                'test cases'
            )

        member = Member(
            service.service_id, account,
            local_service_contract=local_service_contract
        )
        await member.setup(local_service_contract=local_service_contract)

        if member_id:
            if isinstance(member_id, str):
                member.member_id = UUID(member_id)
            elif isinstance(member_id, UUID):
                member.member_id = member_id
            else:
                raise ValueError(f'member_id {member_id} must have type UUID')
        else:
            member.member_id = uuid4()

        if not await member.paths.exists(member.paths.SERVICE_FILE):
            filepath = member.paths.get(member.paths.SERVICE_FILE)

        # TODO: make this more user-friendly by attempting to download
        # the specific version of a schema
        if not local_service_contract:
            await member.service.download_schema(
                save=True, filepath=member.paths.get(Paths.MEMBER_SERVICE_FILE)
            )

        member.schema = await member.load_schema(
            filepath=local_service_contract,
            verify_signatures=not bool(local_service_contract)
        )

        if (member.schema.version != schema_version):
            raise ValueError(
                f'Downloaded schema for service_id {service.service_id} '
                f'has version {member.schema.version} instead of version '
                f'{schema_version} as requested'
            )

        member.tls_secret = MemberSecret(
            member.member_id, member.service_id, member.account
        )
        member.data_secret = MemberDataSecret(
            member.member_id, member.service_id, member.account
        )

        await member.create_secrets(members_ca=members_ca)

        member.data_secret.create_shared_key()

        member.data = MemberData(
            member, member.paths, member.document_store
        )
        member.data.initalize()

        await member.data.save_protected_shared_key()
        await member.data.save()

        filepath = member.paths.get(member.paths.MEMBER_SERVICE_FILE)
        await member.schema.save(filepath, member.paths.storage_driver)

        return member