Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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.
Ejemplo n.º 7
0
Archivo: health.py Proyecto: minet/adh6
    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
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
Archivo: member.py Proyecto: minet/adh6
 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)
Ejemplo n.º 10
0
Archivo: member.py Proyecto: minet/adh6
    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)
Ejemplo n.º 11
0
 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)
Ejemplo n.º 12
0
    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
Ejemplo n.º 13
0
    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))
Ejemplo n.º 14
0
    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
Ejemplo n.º 15
0
 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
Ejemplo n.º 16
0
    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)
Ejemplo n.º 17
0
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
Ejemplo n.º 18
0
 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
Ejemplo n.º 19
0
    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)
Ejemplo n.º 20
0
    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)
Ejemplo n.º 21
0
    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()
Ejemplo n.º 22
0
    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.
Ejemplo n.º 23
0
    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
Ejemplo n.º 24
0
    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