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
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)
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 )
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)
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