def wrapper(cls, ctx, *args, **kwargs): """ Wrap http_api function. """ class_name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', type(cls).__name__) class_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', class_name).lower() log_kwargs = {} log_args = [] for key, value in kwargs.items(): if hasattr(adh6.entity, type(value).__name__): log_kwargs[key] = value.to_dict() else: log_kwargs[key] = value for arg in args: if hasattr(adh6.entity, type(arg).__name__): log_args.append(arg.to_dict()) else: log_args.append(arg) log_kwargs["__args"] = log_args LOG.info(class_name + "_" + f.__name__ + "_called", extra=log_extra(ctx, **log_kwargs)) return f(cls, ctx, *args, **kwargs)
def update_or_create(self, ctx, abstract_transaction: AbstractTransaction, id: Optional[int] = None) -> Tuple[Transaction, bool]: if abstract_transaction.src == abstract_transaction.dst: raise ValidationError('the source and destination accounts must not be the same') if abstract_transaction.value is None: raise ValidationError('the value field should not be None') if abstract_transaction.value <= 0: raise IntMustBePositive('value') if Roles.TRESO_WRITE.value not in ctx.get(CTX_ROLES): abstract_transaction.pending_validation = True transaction, created = super().update_or_create(ctx, abstract_transaction, id=id) if created: LOG.info('cashbox_update', extra=log_extra( ctx, value_modifier=abstract_transaction.value, transaction=transaction, )) if transaction.cashbox == "to": self.cashbox_repository.update(ctx, value_modifier=transaction.value, transaction=transaction) elif transaction.cashbox == "from": self.cashbox_repository.update(ctx, value_modifier=-transaction.value, transaction=transaction) return transaction, created
def create(self, ctx, body: SubscriptionBody, state: MembershipStatus) -> Membership: """ Add a membership record. :raise MemberNotFound """ now = datetime.now() session: Session = ctx.get(CTX_SQL_SESSION) LOG.debug("sql_membership_repository_add_membership_called", extra=log_extra(ctx)) to_add = MembershipSQL( uuid=str(uuid.uuid4()), duration=body.duration, account_id=body.account, payment_method_id=body.payment_method, adherent_id=body.member, status=state, create_at=now, update_at=now, first_time=session.query(MembershipSQL).filter( MembershipSQL.adherent_id == body.member).count() == 0) session.add(to_add) session.flush() LOG.debug("sql_membership_repository_add_membership_finished", extra=log_extra(ctx, membership_uuid=to_add.uuid)) return _map_membership_sql_to_entity(to_add)
def get_logs(self, ctx, member_id, dhcp=False) -> List[str]: """ User story: As an admin, I can retrieve the logs of a member, so I can help him troubleshoot their connection issues. :raise MemberNotFound """ # Check that the user exists in the system. member = self.member_repository.get_by_id(ctx, member_id) if not member: raise MemberNotFoundError(member_id) # Do the actual log fetching. try: devices = self.device_repository.search_by( ctx, limit=100, offset=0, device_filter=DeviceFilter(member=member.id))[0] logs = self.logs_repository.get_logs(ctx, username=member.username, devices=devices, dhcp=dhcp) return list(map(lambda x: "{} {}".format(x[0], x[1]), logs)) except LogFetchError: LOG.warning("log_fetch_failed", extra=log_extra(ctx, username=member.username)) return [] # We fail open here.
def health(self, ctx): LOG.debug("http_health_called", extra=log_extra(ctx)) if self.health_manager.is_healthy(ctx): return "OK", 200 else: return "FAILURE", 503
def get_cashbox(self, ctx) -> Tuple[int, int]: fond, coffre = self.cashbox_repository.get(ctx) # Log action. LOG.info('cashbox_get', extra=log_extra( ctx )) return fond, coffre
def subscription_patch(self, ctx, id_, body): try: LOG.debug("membership_patch_called", extra=log_extra(ctx, body=body, id=id_)) to_update = SubscriptionBody.from_dict(body) self.member_manager.update_subscription(ctx, id_, to_update) return NoContent, 204 except Exception as e: return handle_error(ctx, e)
def is_healthy(self, ctx) -> bool: db_health = self.health_repository.ping(ctx) if not db_health: LOG.error("health_check_db_not_healthy", extra=log_extra(ctx)) return False # TODO: add more health checks? LOG.info("health_check_success", extra=log_extra(ctx)) return True
def subscription_post(self, ctx, id_: int, body: dict): """ Add a membership record in the database """ LOG.debug("http_member_post_membership_called", extra=log_extra(ctx, id=id_, request=body)) try: created_membership = self.member_manager.create_subscription( ctx, id_, SubscriptionBody.from_dict(body)) return created_membership.to_dict(), 200 # 200 OK except Exception as e: return handle_error(ctx, e)
def __init__(self): from flask import current_app self.config = current_app.config if 'ELK_HOSTS' not in self.config: return LOG.info('About to instantiate ElasticSearch') LOG.debug('ELK_HOSTS:' + str(self.config['ELK_HOSTS'])) self.es = Elasticsearch(self.config['ELK_HOSTS'], http_auth=(self.config['ELK_USER'], self.config['ELK_SECRET']))
def vlan_put(self, ctx, id_, body): LOG.debug("http_port_vlan_put_called", extra=log_extra(ctx, id=id_)) try: if (self.switch_network_manager.get_port_vlan(ctx, port_id=id_) ) == "No Such Instance currently exists at this OID": return 1, 200 self.switch_network_manager.update_port_vlan(ctx, port_id=id_, vlan=int(body)) return int(body), 204 except Exception as e: return handle_error(ctx, e)
def create(self, ctx, body: MemberBody) -> Member: LOG.debug("create_member_records", extra=log_extra(ctx, username=body.username)) # Check that the user exists in the system. fetched_member = self.member_repository.get_by_login( ctx, body.username) if fetched_member: raise MemberAlreadyExist(fetched_member.username) fetched_account_type, _ = self.account_type_repository.search_by( ctx, terms="Adhérent") if not fetched_account_type: raise AccountTypeNotFoundError("Adhérent") created_member = self.member_repository.create( ctx=ctx, object_to_create=AbstractMember( id=0, username=body.username, first_name=body.first_name, last_name=body.last_name, email=body.mail, departure_date=datetime.now(), ip='', subnet='', comment='', membership=MembershipStatus.INITIAL.value)) self.mailinglist_repository.update_from_member(ctx, created_member.id, 249) _ = self.account_repository.create( ctx, AbstractAccount( id=0, actif=True, account_type=fetched_account_type[0].id, member=created_member.id, name= f'{created_member.first_name} {created_member.last_name} ({created_member.username})', pinned=False, compte_courant=False, balance=0, pending_balance=0)) _ = self.create_subscription( ctx=ctx, member_id=created_member.id, body=SubscriptionBody(member=created_member.id), ) return created_member
def add_membership_payment_record(self, ctx, membership: Membership, free: bool): LOG.debug("membership_add_membership_payment_record", extra=log_extra(ctx, duration=membership.duration, membership_accoun=membership.account)) if free and not Roles.TRESO_WRITE.value in ctx.get(CTX_ROLES): raise UnauthorizedError( "Impossibilité de faire une cotisation gratuite") payment_method = self.payment_method_repository.get_by_id( ctx, membership.payment_method) asso_account, _ = self.account_repository.search_by( ctx, limit=1, filter_=AbstractAccount( name=KnownAccountExpense.ASSOCIATION_EXPENCE.value)) if len(asso_account) != 1: raise AccountNotFoundError( KnownAccountExpense.ASSOCIATION_EXPENCE.value) tech_account, _ = self.account_repository.search_by( ctx, limit=1, filter_=AbstractAccount( name=KnownAccountExpense.TECHNICAL_EXPENSE.value)) if len(tech_account) != 1: raise AccountNotFoundError( KnownAccountExpense.TECHNICAL_EXPENSE.value) src_account = self.account_repository.get_by_id( ctx, membership.account) price = self.duration_price[membership.duration] # Expressed in EUR. if price == 50 and not membership.has_room: price = 9 duration_str = self.duration_string.get(membership.duration) title = f'Internet - {duration_str}' self.transaction_repository.create( ctx, AbstractTransaction(value=9 if not free else 0, src=src_account.id, dst=asso_account[0].id, name=title + " (gratuit)" if free else title, payment_method=payment_method.id)) if price > 9 and not free: self.transaction_repository.create( ctx, AbstractTransaction(value=price - 9, src=src_account.id, dst=tech_account[0].id, name=title, payment_method=payment_method.id))
def ping(self, ctx) -> bool: LOG.debug("sql_ping", extra=log_extra(ctx)) session: Session = ctx.get(CTX_SQL_SESSION) try: result = session.execute('SELECT 42 AS result').fetchall() if 1 != len(result): return False return [(42, )] == result except SQLAlchemyError: return False
def vlan_get(self, ctx, id_): LOG.debug("http_port_vlan_get_called", extra=log_extra(ctx, id=id_)) try: if (self.switch_network_manager.get_port_vlan(ctx, port_id=id_) ) == "No Such Instance currently exists at this OID": return 1, 200 return int( self.switch_network_manager.get_port_vlan(ctx, port_id=id_)), 200 except SwitchNotFoundError: return NoContent, 404 except PortNotFoundError: return NoContent, 404
def handle_error(ctx, e: Exception): if isinstance(e, NotFoundError): return _error(404, str(e)), 404 elif isinstance(e, UnauthorizedError): return _error(403, str(e)), 403 elif isinstance( e, ValueError) or isinstance(e, ValidationError) or isinstance( e, NetworkManagerReadError) or isinstance( e, AlreadyExistsError): return _error(400, str(e)), 400 else: LOG.error('Fatal exception: ' + str(e), extra=log_extra(ctx)) return _error(500, "The server encountered an unexpected error"), 500
def latest_subscription(self, ctx, member_id: int) -> Union[Membership, None]: LOG.debug("get_latest_membership_records", extra=log_extra(ctx, id=member_id)) subscriptions, _ = self.membership_repository.search( ctx=ctx, filter_=AbstractMembership(member=member_id)) if not subscriptions: return None if n := next( filter( lambda x: not self.is_subscription_finished( MembershipStatus(x.status)), subscriptions), None): return n
def get_vlan(self, ctx, vlan_number: int) -> AbstractVlan: """ Get a VLAN. :raise VlanNotFound """ LOG.debug("vlan_sql_repository_get_vlan", extra=log_extra(ctx, vlan_number=vlan_number)) session: Session = ctx.get(CTX_SQL_SESSION) vlan = session.query(VlanSQL).filter( VlanSQL.numero == vlan_number).one_or_none() if not vlan: raise VLANNotFoundError(vlan_number) return _map_vlan_sql_to_abstract_entity(vlan)
def apikey_auth(token: str, required_scopes): try: from hashlib import sha3_512 api_key = api_key_repository.find(token_hash=sha3_512(token.encode("utf-8")).hexdigest())[0] if not api_key or not api_key.login: raise roles = [ i.role for i in role_repository.find( method=AuthenticationMethod.API_KEY, identifiers=[str(api_key.id) if api_key.id else ""] )[0] ] if len(set(roles)&set(required_scopes)) != len(required_scopes): raise Unauthorized('invalid api key') except Exception as e: LOG.info(e) raise Unauthorized('invalid api key') return { "uid": role_repository.user_id_from_username(login=api_key.login), "scope": roles }
def search( self, ctx, limit=DEFAULT_LIMIT, offset=DEFAULT_OFFSET, terms=None, filter_: Optional[AbstractMembership] = None ) -> Tuple[List[Membership], int]: LOG.debug("sql_membership_repository_search_membership_called", extra=log_extra(ctx)) session: Session = ctx.get(CTX_SQL_SESSION) query = session.query(MembershipSQL) if filter_: if filter_.uuid is not None: query = query.filter(MembershipSQL.uuid == filter_.uuid) if filter_.status is not None: query = query.filter(MembershipSQL.status == filter_.status) if filter_.first_time is not None: query = query.filter( MembershipSQL.first_time == filter_.first_time) if filter_.duration is not None: query = query.filter( MembershipSQL.duration == filter_.duration) if filter_.payment_method is not None: query = query.filter( MembershipSQL.payment_method_id == filter_.payment_method) if filter_.account is not None: query = query.filter( MembershipSQL.account_id == filter_.account) if filter_.member is not None: query = query.filter( MembershipSQL.adherent_id == filter_.member) query = query.order_by(MembershipSQL.uuid) query = query.offset(offset) query = query.limit(limit) r = query.all() return list(map(_map_membership_sql_to_entity, r)), query.count()
def update(self, ctx, uuid: str, body: SubscriptionBody, state: MembershipStatus) -> Membership: LOG.debug("sql_membership_repository_update_membership_called", extra=log_extra(ctx, uuid=uuid)) now = datetime.now() session: Session = ctx.get(CTX_SQL_SESSION) query: Query = session.query(MembershipSQL).filter( MembershipSQL.uuid == uuid) membership: MembershipSQL = query.one() if body.duration: membership.duration = body.duration if body.account: membership.account_id = body.account if body.payment_method: membership.payment_method_id = body.payment_method membership.status = state membership.update_at = now session.flush() return _map_membership_sql_to_entity(membership)
def wrapper(*args,**kwargs): """ Wrap http_api function. """ s = session_handler.session() if session_handler else db.session() if "token_info" not in connexion.context: LOG.warning('could_not_extract_token_info_kwargs') raise UnauthenticatedError("Not token informations") token_info = connexion.context["token_info"] testing = current_app.config["TESTING"] ctx = build_context( session=s, testing=testing, request_id=str(uuid.uuid4()), # Generates an unique ID on this request so we can track it. url=request.url, admin=token_info.get("uid", ""), roles=token_info.get("scope", []) ) kwargs["ctx"] = ctx try: result = f(*args, **kwargs) # It makes things clearer and less error-prone. if not isinstance(result, tuple) or len(result) <= 1: raise ValueError("Please always pass the result AND the HTTP code.") status_code = result[1] msg = result[0] if result[0] == NoContent: msg = None if status_code and (200 <= status_code <= 299 or status_code == 302): s.commit() else: LOG.info("rollback_sql_transaction_non_200_http_code", extra=log_extra(ctx, code=status_code, message=msg)) s.rollback() return result except Exception as e: LOG.error("rollback_sql_transaction_exception_caught", extra=log_extra(ctx, exception=str(e), traceback=traceback.format_exc())) s.rollback() raise finally: # When running unit tests, we don't close the session so tests can actually perform some work on that # session. if not testing: s.close()
def create(self, ctx, abstract_account: Account) -> object: session: Session = ctx.get(CTX_SQL_SESSION) LOG.debug("sql_account_repository_create_called", extra=log_extra(ctx, account_type=abstract_account.account_type)) now = datetime.now() account_type_query = session.query(AccountType) if abstract_account.account_type is not None: LOG.debug("sql_account_repository_search_account_type", extra=log_extra(ctx, account_type=abstract_account.account_type)) account_type_query = account_type_query.filter(AccountType.id == abstract_account.account_type) else: account_type_query = account_type_query.filter(AccountType.name == "Adherent") account_type = account_type_query.one_or_none() if account_type is None: raise AccountNotFoundError(abstract_account.account_type) adherent = None if abstract_account.member is not None: adherent = session.query(Adherent).filter(Adherent.id == abstract_account.member).one_or_none() if not adherent: raise MemberNotFoundError(abstract_account.member) account = SQLAccount( name=abstract_account.name, actif=abstract_account.actif, type=account_type.id, creation_date=now, compte_courant=abstract_account.compte_courant, pinned=abstract_account.pinned, adherent_id=adherent.id if adherent else None ) with track_modifications(ctx, session, account): session.add(account) session.flush() LOG.debug("sql_account_repository_create_finished", extra=log_extra(ctx, account_id=account.id)) return _map_account_sql_to_entity(account)
def get_statuses(self, ctx, member_id) -> List[MemberStatus]: # Check that the user exists in the system. member = self.member_repository.get_by_id(ctx, member_id) if not member: raise MemberNotFoundError(member_id) # Do the actual log fetching. try: devices = self.device_repository.search_by( ctx, limit=100, offset=0, device_filter=DeviceFilter(member=member.id))[0] logs = self.logs_repository.get_logs(ctx, username=member.username, devices=devices, dhcp=False) device_to_statuses = {} last_ok_login_mac = {} def add_to_statuses(status, timestamp, mac): if mac not in device_to_statuses: device_to_statuses[mac] = {} if status not in device_to_statuses[mac] or device_to_statuses[ mac][status].last_timestamp < timestamp: device_to_statuses[mac][status] = MemberStatus( status=status, last_timestamp=timestamp, comment=mac) prev_log = ["", ""] for log in logs: if "Login OK" in log[1]: match = re.search( r'.*?Login OK:\s*\[(.*?)\].*?cli ([a-f0-9|-]+)\).*', log[1]) if match is not None: login, mac = match.group(1), match.group(2).upper() if mac not in last_ok_login_mac or last_ok_login_mac[ mac] < log[0]: last_ok_login_mac[mac] = log[0] if "EAP sub-module failed" in prev_log[1] \ and "mschap: MS-CHAP2-Response is incorrect" in log[1] \ and (prev_log[0] - log[0]).total_seconds() < 1: match = re.search( r'.*?EAP sub-module failed\):\s*\[(.*?)\].*?cli ([a-f0-9\-]+)\).*', prev_log[1]) if match: login, mac = match.group(1), match.group(2).upper() if login != member.username: add_to_statuses("LOGIN_INCORRECT_WRONG_USER", log[0], mac) else: add_to_statuses("LOGIN_INCORRECT_WRONG_PASSWORD", log[0], mac) if 'rlm_python' in log[1]: match = re.search( r'.*?rlm_python: Fail (.*?) ([a-f0-9A-F\-]+) with (.+)', log[1]) if match is not None: login, mac, reason = match.group(1), match.group( 2).upper(), match.group(3) if 'MAC not found and not association period' in reason: add_to_statuses("LOGIN_INCORRECT_WRONG_MAC", log[0], mac) if 'Adherent not found' in reason: add_to_statuses("LOGIN_INCORRECT_WRONG_USER", log[0], mac) if "TLS Alert" in log[ 1]: # @TODO Difference between TLS Alert read and TLS Alert write ?? # @TODO a read access denied means the user is validating the certificate # @TODO a read/write protocol version is ??? # @TODO a write unknown CA means the user is validating the certificate # @TODO a write decryption failed is ??? # @TODO a read internal error is most likely not user-related # @TODO a write unexpected_message is ??? match = re.search( r'.*?TLS Alert .*?\):\s*\[(.*?)\].*?cli ([a-f0-9\-]+)\).*', log[1]) if match is not None: login, mac = match.group(1), match.group(2).upper() add_to_statuses("LOGIN_INCORRECT_SSL_ERROR", log[0], mac) prev_log = log all_statuses = [] for mac, statuses in device_to_statuses.items(): for _, object in statuses.items(): if mac in last_ok_login_mac and object.last_timestamp < last_ok_login_mac[ mac]: continue all_statuses.append(object) return all_statuses except LogFetchError: LOG.warning("log_fetch_failed", extra=log_extra(ctx, username=member.username)) return [] # We fail open here.
def get_logs(self, ctx, devices: List[Device], limit=LOG_DEFAULT_LIMIT, username=None, dhcp: bool = False): """ Get the logs related to the username and to the devices. :param ctx: context :param username: username :param devices: MAC addresses of the devices :param limit: limit result :param dhcp: allow to query DHCP logs or not :return: logs """ if ctx.get(CTX_TESTING ): # Do not actually query elasticsearch if testing... return [[1, "test_log"]] if not self.config['ELK_HOSTS']: raise LogFetchError('no elk host configured') # Prepare the elasticsearch query... if not dhcp: query = { "sort": { '@timestamp': 'desc', # Sort by time }, "query": { "bool": { "filter": { "match": { "program": "radiusd" } }, "should": [ # "should" in a "bool" query basically act as a "OR" { "match": { "radius_user": username } }, # Match every log mentioning this member # rules to match MACs addresses are added in the next chunk of code ], "minimum_should_match": 1, }, }, "_source": ["@timestamp", "message", "src_mac" ], # discard any other field than timestamp & message "size": limit, "from": 0, } else: query = { "sort": { '@timestamp': 'desc', # Sort by time }, "query": { "constant_score": { "filter": { "bool": { "should": [], "minimum_should_match": 1, }, }, }, }, "_source": ["@timestamp", "message", "program", "src_mac"], # discard any other field than timestamp & message "size": limit, "from": 0, } # Add the macs to the "should" for d in devices: addr = d.mac variations = map(lambda x: {"match_phrase": { "src_mac": x }}, get_mac_variations(addr)) if not dhcp: # noinspection PyTypeChecker query["query"]["bool"]["should"] += list(variations) else: # noinspection PyTypeChecker query["query"]["constant_score"]["filter"]["bool"][ "should"] += list(variations) LOG.info('About to query ElasticSearch') res = self.es.search(index="", body=query)['hits']['hits'] if not dhcp: for r in res: #msg = re.sub('(?<=incorrect) \(.*(failed|incorrect)\)', '', r["_source"]["message"]) #msg = re.sub('\(from client .* (cli |tunnel)', '', r["_source"]["message"]) #msg = re.sub('\) ', '', msg) #msg = re.sub(' {0}P', ' P', msg) #r["_source"]["message"] = r["_source"]["message"] pass return list( map( lambda x: [ dateutil.parser.parse(x["_source"]["@timestamp"]), x[ "_source"]["message"] ], res))
def update_subscription(self, ctx, member_id: int, body: SubscriptionBody) -> None: member = self.member_repository.get_by_id(ctx, member_id) if not member: raise MemberNotFoundError(member_id) subscription = self.latest_subscription(ctx=ctx, member_id=member_id) if not subscription: raise MembershipNotFoundError() if subscription.status in [ MembershipStatus.COMPLETE, MembershipStatus.CANCELLED, MembershipStatus.ABORTED ]: raise MembershipStatusNotAllowed( subscription.status, "membership already completed, cancelled or aborted") state = MembershipStatus(subscription.status) if state == MembershipStatus.PENDING_RULES: date_signed_minet = self.charter_repository.get( ctx, member_id=member_id, charter_id=1) if date_signed_minet is not None and date_signed_minet != "": LOG.debug( "create_membership_record_switch_status_to_pending_payment_initial" ) state = MembershipStatus.PENDING_PAYMENT_INITIAL else: raise CharterNotSigned(str(member_id)) if body.duration is not None and body.duration != 0: if body.duration not in self.duration_price: LOG.warning("create_membership_record_no_price_defined", extra=log_extra(ctx, duration=body.duration)) raise NoPriceAssignedToThatDuration(body.duration) if state == MembershipStatus.PENDING_PAYMENT_INITIAL: if body.duration is not None: LOG.debug( "create_membership_record_switch_status_to_pending_payment" ) state = MembershipStatus.PENDING_PAYMENT if body.account is not None: account = self.account_repository.get_by_id(ctx, body.account) if not account: raise AccountNotFoundError(body.account) if body.payment_method is not None: payment_method = self.payment_method_repository.get_by_id( ctx, body.payment_method) if not payment_method: raise PaymentMethodNotFoundError(body.payment_method) if state == MembershipStatus.PENDING_PAYMENT: if body.account is not None and body.payment_method is not None: LOG.debug( "create_membership_record_switch_status_to_pending_payment_validation" ) state = MembershipStatus.PENDING_PAYMENT_VALIDATION try: self.membership_repository.update(ctx, subscription.uuid, body, state) except Exception: raise
def create_subscription(self, ctx, member_id: int, body: SubscriptionBody) -> Membership: """ Core use case of ADH. Registers a membership. User story: As an admin, I can create a new membership record, so that a member can have internet access. :param ctx: context :param member_id: member_id :param membership: entity AbstractMembership :raise IntMustBePositiveException :raise NoPriceAssignedToThatDurationException :raise MemberNotFound :raise UnknownPaymentMethod """ member = self.member_repository.get_by_id(ctx, member_id) if not member: raise MemberNotFoundError(member_id) latest_subscription = self.latest_subscription(ctx=ctx, member_id=member_id) if latest_subscription and latest_subscription.status not in [ MembershipStatus.COMPLETE.value, MembershipStatus.CANCELLED.value, MembershipStatus.ABORTED.value ]: raise MembershipAlreadyExist(latest_subscription.status) state = MembershipStatus.PENDING_RULES if state == MembershipStatus.PENDING_RULES: date_signed_minet = self.charter_repository.get( ctx, member_id=member_id, charter_id=1) if date_signed_minet is not None and date_signed_minet != "": LOG.debug( "create_membership_record_switch_status_to_pending_payment_initial" ) state = MembershipStatus.PENDING_PAYMENT_INITIAL if state == MembershipStatus.PENDING_PAYMENT_INITIAL: if body.duration is not None and body.duration != 0: if body.duration not in self.duration_price: LOG.warning("create_membership_record_no_price_defined", extra=log_extra(ctx, duration=body.duration)) raise NoPriceAssignedToThatDuration(body.duration) LOG.debug( "create_membership_record_switch_status_to_pending_payment" ) state = MembershipStatus.PENDING_PAYMENT if state == MembershipStatus.PENDING_PAYMENT: if body.account is not None and body.payment_method is not None: account = self.account_repository.get_by_id(ctx, body.account) if not account: raise AccountNotFoundError(body.account) payment_method = self.payment_method_repository.get_by_id( ctx, body.payment_method) if not payment_method: raise PaymentMethodNotFoundError(body.payment_method) LOG.debug( "create_membership_record_switch_status_to_pending_payment_validation" ) state = MembershipStatus.PENDING_PAYMENT_VALIDATION try: membership_created = self.membership_repository.create( ctx, body, state) except UnknownPaymentMethod: LOG.warning("create_membership_record_unknown_payment_method", extra=log_extra(ctx, payment_method=body.payment_method)) raise LOG.info("create_membership_record", extra=log_extra(ctx, membership_uuis=membership_created.uuid, membership_status=membership_created.status)) return membership_created