Beispiel #1
0
class User(UserMixin, db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    first_name = db.Column(db.String(128), nullable=False)
    last_name = db.Column(db.String(128), nullable=False)
    email = db.Column(db.String(128), nullable=False, unique=True)
    password = db.Column(db.String(128), nullable=False)
    image = db.Column(db.String(128), nullable=False)
    created_at = db.Column(db.DateTime, nullable=False)

    def __init__(self, first_name, last_name, email, password):
        self.first_name = first_name
        self.last_name = last_name
        self.email = email
        self.password = password
        self.image = "http://www.personalbrandingblog.com/wp-content/uploads/2017/08/blank-profile-picture-973460_640-300x300.png"
        self.created_at = datetime.datetime.utcnow()

    def save(self):
        """Add user to database"""
        db.session.add(self)
        db.session.commit()

    """
    updates user's image 
    """
    def update_image(self, image):
        self.image = image
        db.session.add(self)
        db.session.commit()


    """
    updates user's name 
    """
    def update_name(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
        db.session.add(self)
        db.session.commit()

    """
    Get all ingridients for an user

    Returns
    -------
    List of ingridient object
    """
    def get_ingridients(self):
        return Ingredient.query.filter_by(user_id=self.id)

    """
    Add an ingridient

    Parameters
    ----------
    item_name : Str
        Name of ingridient

    """
    def add_ingridient(self, item_name):
        return Ingredient(name=item_name, user_id=self.id).save()

    """
    Remove an ingridient

    Parameters
    ----------
    item_name : Str
        Name of ingridient

    """
    def remove_ingridient(self, item_name):
        Ingredient.query.filter_by(name=item_name, user_id=self.id).first().remove()

    """
    Get all allergies for an user

    Returns
    -------
    List of ingridient object
    """
    def get_allergies(self):
        return Allergy.query.filter_by(user_id=self.id)

    """
    Add an allergy

    Parameters
    ----------
    item_name : Str
        Name of allergy
    """
    def add_allergy(self, item_name):
        return Allergy(name=item_name, user_id=self.id).save()

    """
    Add multiple allergies

    Parameters
    ----------
    item_name : List of Str
        List of names of allergies
    """
    def add_allergies(self, items_name):
        for item_name in items_name:
            self.add_allergy(item_name)


    """
    Remove all allergy for an user

    """
    def remove_all_allergies(self):
        for allergy in Allergy.query.filter_by(user_id=self.id):
            allergy.remove()

    """
    Add pref 

    """
    def add_pref(self, diet_pref, personal_pref):
        for pref in Preference.query.filter_by(user_id=self.id):
            pref.remove()
        return Preference(diet_pref=diet_pref, personal_pref=personal_pref, user_id=self.id).save()


    """
    Remove pref 

    """
    def get_pref(self):
        return Preference.query.filter_by(user_id=self.id)

    """
    Check if the password given is correct or not
    Correct password is exactly same as the one that is stored in the database

    Parameters
    ----------
    pswd : Str
        Password to be checked. e.q. String that user inputs in the log in page.

    Returns
    -------
    Boolean
        True if the password is same. False if the password is NOT same.
    """
    def is_password_correct(self, pswd):
        return self.password == pswd

    def tojson(self):
        """Represent user data as JSON object"""
        return json.dumps({
                'first_name': self.first_name,
                'last_name': self.last_name,
                'email': self.email,
                'password': self.password
                })

    """
    Get one user based on the email

    Parameters
    ----------
    email : Str
        User's email address that you are looking for

    Returns
    -------
    User
        User object filtered by the email
    """
    @staticmethod
    def get_one_user_by_email(email):
        return User.query.filter_by(email=email).first()

    # callback to reload the user object        
    @login_manager.user_loader
    def load_user(id):
        return User.query.filter_by(id=id).first()
class TransferAccount(OneOrgBase, ModelBase):
    __tablename__ = 'transfer_account'

    name            = db.Column(db.String())
    _balance_wei    = db.Column(db.Numeric(27), default=0)
    blockchain_address = db.Column(db.String())

    is_approved     = db.Column(db.Boolean, default=False)

    # These are different from the permissions on the user:
    # is_vendor determines whether the account is allowed to have cash out operations etc
    # is_beneficiary determines whether the account is included in disbursement lists etc
    is_vendor       = db.Column(db.Boolean, default=False)

    is_beneficiary = db.Column(db.Boolean, default=False)

    is_ghost = db.Column(db.Boolean, default=False)

    account_type    = db.Column(db.Enum(TransferAccountType))

    payable_period_type   = db.Column(db.String(), default='week')
    payable_period_length = db.Column(db.Integer, default=2)
    payable_epoch         = db.Column(db.DateTime, default=datetime.datetime.utcnow)

    token_id        = db.Column(db.Integer, db.ForeignKey("token.id"))

    exchange_contract_id = db.Column(db.Integer, db.ForeignKey(ExchangeContract.id))

    transfer_card    = db.relationship('TransferCard', backref='transfer_account', lazy=True, uselist=False)

    # users               = db.relationship('User', backref='transfer_account', lazy=True)
    users = db.relationship(
        "User",
        secondary=user_transfer_account_association_table,
        back_populates="transfer_accounts",
        lazy='joined'
    )

    credit_sends       = db.relationship('CreditTransfer', backref='sender_transfer_account',
                                         foreign_keys='CreditTransfer.sender_transfer_account_id')

    credit_receives    = db.relationship('CreditTransfer', backref='recipient_transfer_account',
                                         foreign_keys='CreditTransfer.recipient_transfer_account_id')

    spend_approvals_given = db.relationship('SpendApproval', backref='giving_transfer_account',
                                            foreign_keys='SpendApproval.giving_transfer_account_id')

    def get_float_transfer_account(self):
        for transfer_account in self.organisation.transfer_accounts:
            if transfer_account.account_type == 'FLOAT':
                return transfer_account

        float_wallet = TransferAccount.query.filter(TransferAccount.account_type == TransferAccountType.FLOAT).first()

        return float_wallet

    @property
    def balance(self):
        # division/multipication by int(1e16) occurs  because
        # the db stores amounts in integer WEI: 1 BASE-UNIT (ETH/USD/ETC) * 10^18
        # while the system passes around amounts in float CENTS: 1 BASE-UNIT (ETH/USD/ETC) * 10^2
        # Therefore the conversion between db and system is 10^18/10^2c = 10^16
        # We use cents for historical reasons, and to enable graceful degredation/rounding on
        # hardware that can only handle small ints (like the transfer cards and old android devices)

        # rounded to whole value of balance
        return float((self._balance_wei or 0) / int(1e16))

    @balance.setter
    def balance(self, val):
        self._balance_wei = val * int(1e16)

    def decrement_balance(self, val):
        self.increment_balance(-1 * val)

    def increment_balance(self, val):
        # self.balance += val
        val_wei = val * int(1e16)
        if isinstance(val_wei, float):
            val_wei = Decimal(val_wei).quantize(Decimal('1'))

        self._balance_wei = (self._balance_wei or 0) + val_wei

    @hybrid_property
    def total_sent(self):
        return int(
            db.session.query(func.sum(server.models.credit_transfer.CreditTransfer.transfer_amount).label('total')).execution_options(show_all=True)
            .filter(server.models.credit_transfer.CreditTransfer.transfer_status == TransferStatusEnum.COMPLETE)
            .filter(server.models.credit_transfer.CreditTransfer.sender_transfer_account_id == self.id).first().total or 0
        )

    @hybrid_property
    def total_received(self):
        return int(
            db.session.query(func.sum(server.models.credit_transfer.CreditTransfer.transfer_amount).label('total')).execution_options(show_all=True)
            .filter(server.models.credit_transfer.CreditTransfer.transfer_status == TransferStatusEnum.COMPLETE)
            .filter(server.models.credit_transfer.CreditTransfer.recipient_transfer_account_id == self.id).first().total or 0
        )

    @hybrid_property
    def primary_user(self):
        if len(self.users) == 0:
            return None
        return self.users[0]
        # users = User.query.execution_options(show_all=True) \
        #     .filter(User.transfer_accounts.any(TransferAccount.id.in_([self.id]))).all()
        # if len(users) == 0:
        #     # This only happens when we've unbound a user from a transfer account by manually editing the db
        #     return None
        #
        # return sorted(users, key=lambda user: user.created)[0]

    @hybrid_property
    def primary_user_id(self):
        return self.primary_user.id

    # rounded balance
    @hybrid_property
    def rounded_account_balance(self):
        return (self._balance_wei or 0) / int(1e18)

    @hybrid_property
    def master_wallet_approval_status(self):

        if not current_app.config['USING_EXTERNAL_ERC20']:
            return 'NOT_REQUIRED'

        if not self.blockchain_address.encoded_private_key:
            return 'NOT_REQUIRED'

        base_query = (
            BlockchainTransaction.query
                .filter(BlockchainTransaction.transaction_type == 'master wallet approval')
                .filter(BlockchainTransaction.credit_transfer.has(recipient_transfer_account_id=self.id))
        )

        successful_transactions = base_query.filter(BlockchainTransaction.status == 'SUCCESS').all()

        if len(successful_transactions) > 0:
            return 'APPROVED'

        requested_transactions = base_query.filter(BlockchainTransaction.status == 'PENDING').all()

        if len(requested_transactions) > 0:
            return 'REQUESTED'

        failed_transactions = base_query.filter(BlockchainTransaction.status == 'FAILED').all()

        if len(failed_transactions) > 0:
            return 'FAILED'

        return 'NO_REQUEST'

    def get_or_create_system_transfer_approval(self):
        sys_blockchain_address = self.organisation.system_blockchain_address

        approval = self.get_approval(sys_blockchain_address)

        if not approval:
            approval = self.give_approval_to_address(sys_blockchain_address)

        return approval

    def give_approval_to_address(self, address_getting_approved):
        approval = SpendApproval(transfer_account_giving_approval=self,
                                 address_getting_approved=address_getting_approved)

        db.session.add(approval)

        return approval

    def get_approval(self, receiving_address):
        for approval in self.spend_approvals_given:
            if approval.receiving_address == receiving_address:
                return approval
        return None

    def approve_and_disburse(self, initial_disbursement=None):
        from server.utils.access_control import AccessControl

        active_org = getattr(g, 'active_organisation', self.primary_user.default_organisation)
        admin = getattr(g, 'user', None)
        auto_resolve = initial_disbursement == active_org.default_disbursement

        if not self.is_approved and admin and AccessControl.has_sufficient_tier(admin.roles, 'ADMIN', 'admin'):
            self.is_approved = True

        if self.is_beneficiary:
            # TODO: make this more robust
            # approve_and_disburse might be called for a second time to disburse
            # so first check that no credit transfer have already been received
            if len(self.credit_receives) < 1:
                # make initial disbursement
                disbursement = self._make_initial_disbursement(initial_disbursement, auto_resolve)
                return disbursement

            elif len(self.credit_receives) == 1:
                # else likely initial disbursement received, check if DISBURSEMENT and PENDING and resolve if default

                disbursement = self.credit_receives[0]
                if disbursement.transfer_subtype == TransferSubTypeEnum.DISBURSEMENT and disbursement.transfer_status == TransferStatusEnum.PENDING and auto_resolve:
                    disbursement.resolve_as_completed()
                    return disbursement

    def _make_initial_disbursement(self, initial_disbursement, auto_resolve=False):
        from server.utils.credit_transfer import make_payment_transfer

        active_org = getattr(g, 'active_organisation', Organisation.master_organisation())
        initial_disbursement = initial_disbursement or active_org.default_disbursement
        if not initial_disbursement:
            return None

        user_id = get_authorising_user_id()
        if user_id is not None:
            sender = User.query.execution_options(show_all=True).get(user_id)
        else:
            sender = self.primary_user

        disbursement = make_payment_transfer(
            initial_disbursement, token=self.token, send_user=sender, receive_user=self.primary_user,
            transfer_subtype=TransferSubTypeEnum.DISBURSEMENT, is_ghost_transfer=False, require_sender_approved=False,
            require_recipient_approved=False, automatically_resolve_complete=auto_resolve)

        return disbursement

    def initialise_withdrawal(self, withdrawal_amount):
        from server.utils.credit_transfer import make_withdrawal_transfer
        withdrawal = make_withdrawal_transfer(withdrawal_amount,
                                              send_account=self,
                                              automatically_resolve_complete=False)
        return withdrawal

    def _bind_to_organisation(self, organisation):
        if not self.organisation:
            self.organisation = organisation
        if not self.token:
            self.token = organisation.token

    def __init__(self,
                 blockchain_address: Optional[str]=None,
                 bound_entity: Optional[Union[Organisation, User]]=None,
                 account_type: Optional[TransferAccountType]=None,
                 private_key: Optional[str] = None,
                 **kwargs):

        super(TransferAccount, self).__init__(**kwargs)

        if bound_entity:
            bound_entity.transfer_accounts.append(self)

            if isinstance(bound_entity, Organisation):
                self.account_type = TransferAccountType.ORGANISATION
                self.blockchain_address = bound_entity.primary_blockchain_address

                self._bind_to_organisation(bound_entity)

            elif isinstance(bound_entity, User):
                self.account_type = TransferAccountType.USER
                self.blockchain_address = bound_entity.primary_blockchain_address

                if bound_entity.default_organisation:
                    self._bind_to_organisation(bound_entity.default_organisation)

            elif isinstance(bound_entity, ExchangeContract):
                self.account_type = TransferAccountType.CONTRACT
                self.blockchain_address = bound_entity.blockchain_address
                self.is_public = True
                self.exchange_contact = self

        if not self.organisation:
            master_organisation = Organisation.master_organisation()
            if not master_organisation:
                raise Exception('master_organisation not found')

            self._bind_to_organisation(master_organisation)

        if blockchain_address:
            self.blockchain_address = blockchain_address

        if not self.blockchain_address:
            self.blockchain_address = bt.create_blockchain_wallet(private_key=private_key)

        if account_type:
            self.account_type = account_type
Beispiel #3
0
class System(db.Model):
    __tablename__ = 'system'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    address = db.Column(db.String(255))
    owner = db.Column(db.String(255))
    phone = db.Column(db.Integer)
    email = db.Column(db.String(255))
    nit_1 = db.Column(db.Integer)
    nit_2 = db.Column(db.Integer)
    logo = db.Column(db.LargeBinary)
    sign = db.Column(db.LargeBinary)
    id_company_legal = db.Column(db.Integer, default=0)
    secuence_contract = db.Column(db.Integer, default=0)
    secuence_payroll = db.Column(db.Integer, default=0)
    secuence_vehicle = db.Column(db.Integer, default=0)

    def __init__(self,
                 name=None,
                 address=None,
                 owner=None,
                 phone=None,
                 email=None,
                 nit_1=None,
                 nit_2=False,
                 logo=None,
                 sign=None,
                 id_company_legal=None,
                 secuence_contract=False,
                 secuence_payroll=False,
                 secuence_vehicle=False):
        if name:
            self.name = name.lower()
        if address:
            self.address = address.lower()
        if owner:
            self.owner = owner.lower()
        if phone:
            self.phone = phone
        if email:
            self.email = email.lower()
        if nit_1:
            self.nit_1 = nit_1
        if nit_2:
            self.nit_2 = nit_2
        if logo:
            self.logo = logo
        if sign:
            self.sign = sign
        if id_company_legal:
            self.id_company_legal = id_company_legal
        if secuence_contract:
            self.secuence_contract = secuence_contract
        if secuence_payroll:
            self.secuence_payroll = secuence_payroll
        if secuence_vehicle:
            self.secuence_vehicle = secuence_vehicle

    def get_json(self):
        return dict(name=self.name,
                    address=self.address,
                    owner=self.owner,
                    phone=self.phone,
                    email=self.email,
                    nit_1=self.nit_1,
                    nit_2=self.nit_2,
                    logo=image_format(self.logo),
                    sign=image_format(self.sign),
                    id_company_legal=self.id_company_legal,
                    secuence_contract=self.secuence_contract,
                    secuence_payroll=self.secuence_payroll,
                    secuence_vehicle=self.secuence_vehicle)
Beispiel #4
0
class Playlists(db.Model):
    id = db.Column(db.Integer, primary_key=True)                                # id of the playlist
    name = db.Column(db.String(80), unique=False, nullable=False, default="New playlist")
    creation_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) # Creation timestamp
    edit_date = db.Column(db.DateTime, default=datetime.utcnow)                 # Last time the playlist was edited (to update: datetime.datetime.utcnow())
    version = db.Column(db.Integer, default=0)                  # Incremental version number: +1 every time the playlist is saved
           
    def save(self):
        self.edit_date = datetime.utcnow()
        self.version += 1
        db.session.commit()

    def add_element(self, elements):
        if isinstance(elements, str):
            elements = json.loads(elements)
        if not isinstance(elements, list):
            elements = [elements]
        for i in elements:
            if "id" in i:   # delete old ids to mantain the new sorting scheme (the elements list should be already ordered, for this reason we clear the elements and add them in the right order)
                del i["id"]
            if not isinstance(i, GenericPlaylistElement):
                i = GenericPlaylistElement.create_element_from_dict(i)
            i.save(self._ec())
        db.session.commit()
    
    def clear_elements(self):
        return self._ec().clear_elements()

    def get_elements(self):
        els = self._ec().get_playlist_elements()
        res = []
        for e in els:
            res.append(GenericPlaylistElement.create_element_from_db(e))
        return res
    
    def get_elements_json(self):
        els = self.get_elements()
        return json.dumps([e.get_dict() for e in els])

    def to_json(self):
        return json.dumps({
            "name": self.name,
            "elements": self.get_elements_json(),
            "id": self.id,
            "version": self.version
        })

    # returns the database table class for the elements of that playlist
    def _ec(self):
        if not hasattr(self, "_tc"):
            self._tc = get_playlist_table_class(self.id)
        return self._tc
            
    @classmethod
    def create_playlist(cls):
        item = Playlists()
        db.session.add(item)
        db.session.commit()
        create_playlist_table(item.id)
        return item
    
    @classmethod
    def get_playlist(cls, id):
        if id is None:
            raise ValueError("An id is necessary to select a playlist")
        try:
            return db.session.query(Playlists).filter(Playlists.id==id).one()       # todo check if there is at leas one line (if the playlist exist)
        except:
            return Playlists.create_playlist()

    @classmethod
    def delete_playlist(cls, id):
        item = db.session.query(Playlists).filter_by(id=id).first()
        db.session.delete(item)
        db.session.commit()
        delete_playlist_table(id)
Beispiel #5
0
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(255), unique=True)
    registration_datetime = db.Column(db.DateTime,
                                      default=datetime.datetime.utcnow())
Beispiel #6
0
class CreditTransfer(ManyOrgBase, BlockchainTaskableBase):
    __tablename__ = 'credit_transfer'

    uuid = db.Column(db.String, unique=True)

    resolved_date = db.Column(db.DateTime)
    _transfer_amount_wei = db.Column(db.Numeric(27), default=0)

    transfer_type = db.Column(db.Enum(TransferTypeEnum), index=True)
    transfer_subtype = db.Column(db.Enum(TransferSubTypeEnum))
    transfer_status = db.Column(db.Enum(TransferStatusEnum),
                                default=TransferStatusEnum.PENDING)
    transfer_mode = db.Column(db.Enum(TransferModeEnum))
    transfer_use = db.Column(JSON)

    transfer_metadata = db.Column(JSONB)

    exclude_from_limit_calcs = db.Column(db.Boolean, default=False)

    resolution_message = db.Column(db.String())

    token_id = db.Column(db.Integer, db.ForeignKey(Token.id))

    sender_transfer_account_id = db.Column(
        db.Integer, db.ForeignKey("transfer_account.id"))
    recipient_transfer_account_id = db.Column(
        db.Integer, db.ForeignKey("transfer_account.id"))

    sender_blockchain_address_id = db.Column(
        db.Integer, db.ForeignKey("blockchain_address.id"))
    recipient_blockchain_address_id = db.Column(
        db.Integer, db.ForeignKey("blockchain_address.id"))

    sender_user_id = db.Column(db.Integer,
                               db.ForeignKey("user.id"),
                               index=True)
    recipient_user_id = db.Column(db.Integer, db.ForeignKey("user.id"))

    attached_images = db.relationship('UploadedResource',
                                      backref='credit_transfer',
                                      lazy=True)

    fiat_ramp = db.relationship('FiatRamp',
                                backref='credit_transfer',
                                lazy=True,
                                uselist=False)

    __table_args__ = (Index('updated_index', "updated"), )

    from_exchange = db.relationship('Exchange',
                                    backref='from_transfer',
                                    lazy=True,
                                    uselist=False,
                                    foreign_keys='Exchange.from_transfer_id')

    to_exchange = db.relationship('Exchange',
                                  backref='to_transfer',
                                  lazy=True,
                                  uselist=False,
                                  foreign_keys='Exchange.to_transfer_id')

    # TODO: Apply this to all transfer amounts/balances, work out the correct denominator size
    @hybrid_property
    def transfer_amount(self):
        return (self._transfer_amount_wei or 0) / int(1e16)

    @transfer_amount.setter
    def transfer_amount(self, val):
        self._transfer_amount_wei = val * int(1e16)

    def send_blockchain_payload_to_worker(self,
                                          is_retry=False,
                                          queue='high-priority'):
        sender_approval = self.sender_transfer_account.get_or_create_system_transfer_approval(
        )

        recipient_approval = self.recipient_transfer_account.get_or_create_system_transfer_approval(
        )
        self.blockchain_task_uuid = bt.make_token_transfer(
            signing_address=self.sender_transfer_account.organisation.
            system_blockchain_address,
            token=self.token,
            from_address=self.sender_transfer_account.blockchain_address,
            to_address=self.recipient_transfer_account.blockchain_address,
            amount=self.transfer_amount,
            prior_tasks=list(
                filter(lambda x: x is not None, [
                    sender_approval.eth_send_task_uuid,
                    sender_approval.approval_task_uuid,
                    recipient_approval.eth_send_task_uuid,
                    recipient_approval.approval_task_uuid
                ])),
            queue=queue)

    def resolve_as_completed(self,
                             existing_blockchain_txn=None,
                             queue='high-priority'):
        if self.transfer_status not in [None, TransferStatusEnum.PENDING]:
            raise Exception(
                f'Transfer resolve function called multiple times for transaciton {self.id}'
            )
        self.check_sender_transfer_limits()
        self.resolved_date = datetime.datetime.utcnow()
        self.transfer_status = TransferStatusEnum.COMPLETE
        self.sender_transfer_account.decrement_balance(self.transfer_amount)
        self.recipient_transfer_account.increment_balance(self.transfer_amount)

        if self.transfer_type == TransferTypeEnum.PAYMENT and self.transfer_subtype == TransferSubTypeEnum.DISBURSEMENT:
            if self.recipient_user and self.recipient_user.transfer_card:
                self.recipient_user.transfer_card.update_transfer_card()

        if self.fiat_ramp and self.transfer_type in [
                TransferTypeEnum.DEPOSIT, TransferTypeEnum.WITHDRAWAL
        ]:
            self.fiat_ramp.resolve_as_completed()
        if not existing_blockchain_txn:
            self.send_blockchain_payload_to_worker(queue=queue)

    def resolve_as_rejected(self, message=None):
        if self.transfer_status not in [None, TransferStatusEnum.PENDING]:
            raise Exception(
                f'Transfer resolve function called multiple times for transaciton {self.id}'
            )

        if self.fiat_ramp and self.transfer_type in [
                TransferTypeEnum.DEPOSIT, TransferTypeEnum.WITHDRAWAL
        ]:
            self.fiat_ramp.resolve_as_rejected()

        self.resolved_date = datetime.datetime.utcnow()
        self.transfer_status = TransferStatusEnum.REJECTED

        if message:
            self.resolution_message = message

    def get_transfer_limits(self):
        import server.utils.transfer_limits

        return server.utils.transfer_limits.get_transfer_limits(self)

    def check_sender_transfer_limits(self):
        if self.sender_user is None:
            # skip if there is no sender, which implies system send
            return

        relevant_transfer_limits = self.get_transfer_limits()

        for limit in relevant_transfer_limits:

            if limit.no_transfer_allowed:
                raise NoTransferAllowedLimitError(token=self.token.name)

            if limit.transfer_count is not None:
                # GE Limits
                transaction_count = limit.apply_all_filters(
                    self,
                    db.session.query(
                        func.count(CreditTransfer.id).label('count'))
                ).execution_options(show_all=True).first().count

                if (transaction_count or 0) > limit.transfer_count:
                    message = 'Account Limit "{}" reached. Allowed {} transaction per {} days'\
                        .format(limit.name, limit.transfer_count, limit.time_period_days)
                    self.resolve_as_rejected(message=message)
                    raise TransferCountLimitError(
                        transfer_count_limit=limit.transfer_count,
                        limit_time_period_days=limit.time_period_days,
                        token=self.token.name,
                        message=message)

            if limit.transfer_balance_fraction is not None:
                allowed_transfer = limit.transfer_balance_fraction * self.sender_transfer_account.balance

                if self.transfer_amount > allowed_transfer:
                    message = 'Account % Limit "{}" reached. {} available'.format(
                        limit.name, max(allowed_transfer, 0))
                    self.resolve_as_rejected(message=message)
                    raise TransferBalanceFractionLimitError(
                        transfer_balance_fraction_limit=limit.
                        transfer_balance_fraction,
                        transfer_amount_avail=int(allowed_transfer),
                        limit_time_period_days=limit.time_period_days,
                        token=self.token.name,
                        message=message)

            if limit.total_amount is not None:
                # Sempo Compliance Account Limits

                transaction_volume = limit.apply_all_filters(
                    self,
                    db.session.query(
                        func.sum(CreditTransfer.transfer_amount).label('total')
                    )).execution_options(show_all=True).first().total or 0

                if transaction_volume > limit.total_amount:
                    # Don't include the current transaction when reporting amount available
                    amount_avail = limit.total_amount - transaction_volume + int(
                        self.transfer_amount)

                    message = 'Account Limit "{}" reached. {} available'.format(
                        limit.name, max(amount_avail, 0))
                    self.resolve_as_rejected(message=message)
                    raise TransferAmountLimitError(
                        transfer_amount_limit=limit.total_amount,
                        transfer_amount_avail=amount_avail,
                        limit_time_period_days=limit.time_period_days,
                        token=self.token.name,
                        message=message)

        return relevant_transfer_limits

    def check_sender_has_sufficient_balance(self):
        return self.sender_user and self.sender_transfer_account.balance - self.transfer_amount >= 0

    def check_sender_is_approved(self):
        return self.sender_user and self.sender_transfer_account.is_approved

    def check_recipient_is_approved(self):
        return self.recipient_user and self.recipient_transfer_account.is_approved

    def _select_transfer_account(self, token, user):
        if token is None:
            raise Exception("Token must be specified")
        return find_transfer_accounts_with_matching_token(user, token)

    def append_organisation_if_required(self, organisation):
        if organisation and organisation not in self.organisations:
            self.organisations.append(organisation)

    def __init__(self,
                 amount,
                 token=None,
                 sender_user=None,
                 recipient_user=None,
                 sender_transfer_account=None,
                 recipient_transfer_account=None,
                 transfer_type: TransferTypeEnum = None,
                 uuid=None,
                 transfer_metadata=None,
                 fiat_ramp=None,
                 transfer_subtype: TransferSubTypeEnum = None,
                 transfer_mode: TransferModeEnum = None,
                 is_ghost_transfer=False):

        if amount < 0:
            raise Exception("Negative amount provided")
        self.transfer_amount = amount

        self.sender_user = sender_user
        self.recipient_user = recipient_user

        self.sender_transfer_account = sender_transfer_account or self._select_transfer_account(
            token, sender_user)

        self.token = token or self.sender_transfer_account.token

        self.fiat_ramp = fiat_ramp

        try:
            self.recipient_transfer_account = recipient_transfer_account or self._select_transfer_account(
                self.token, recipient_user)

            if is_ghost_transfer is False:
                self.recipient_transfer_account.is_ghost = False
        except NoTransferAccountError:
            self.recipient_transfer_account = TransferAccount(
                bound_entity=recipient_user,
                token=token,
                is_approved=True,
                is_ghost=is_ghost_transfer)
            db.session.add(self.recipient_transfer_account)

        if transfer_type is TransferTypeEnum.DEPOSIT:
            self.sender_transfer_account = self.recipient_transfer_account.get_float_transfer_account(
            )

        if transfer_type is TransferTypeEnum.WITHDRAWAL:
            self.recipient_transfer_account = self.sender_transfer_account.get_float_transfer_account(
            )

        if self.sender_transfer_account.token != self.recipient_transfer_account.token:
            raise Exception("Tokens do not match")

        self.transfer_type = transfer_type
        self.transfer_subtype = transfer_subtype
        self.transfer_mode = transfer_mode
        self.transfer_metadata = transfer_metadata

        if uuid is not None:
            self.uuid = uuid

        self.append_organisation_if_required(
            self.recipient_transfer_account.organisation)
        self.append_organisation_if_required(
            self.sender_transfer_account.organisation)
class Country(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(30), nullable=False)
    code = db.Column(db.String(3), nullable=False)
Beispiel #8
0
class Transaction(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
    ticket_amount = db.Column(db.Integer)
    activity = db.Column(db.String(255))
    datetime = db.Column(db.DateTime, default=datetime.datetime.utcnow())
Beispiel #9
0
class User(UserMixin, db.Model):

    __table_args__ = {'extend_existing': True}
    id = db.Column('id', db.Integer, primary_key=True)
    username = db.Column('username', db.String(), unique=False, index=True)
    email = db.Column('email', db.String(), unique=True, index=True)
    password = db.Column('password', db.String())
    registered_on = db.Column('registered_on',
                              db.DateTime(timezone=True),
                              server_default=func.now())
    notes = db.relationship('Notes',
                            backref='author',
                            lazy='dynamic',
                            cascade="all, delete-orphan")
    trashed_notes = db.relationship('Trash',
                                    backref='author',
                                    lazy='dynamic',
                                    cascade="all, delete-orphan")

    def set_password(self, password):
        self.password = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password, password)

    def generate_confirmation_token(self, expiration=3600):
        s = Serializer(current_app.config['SECRET_KEY'], expiration)
        return s.dumps({'confirm': self.id})

    def confirm(self, token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except:
            return False
        if data.get('confirm') != self.id:
            return False
        self.confirmed = True
        db.session.add(self)
        return True

    def avatar(self, size=128):
        return 'https://www.gravatar.com/avatar/?d=monsterid&s={}'.format(size)

    @property
    def _links(self):
        return {'avatar': self.avatar(128)}

    def get_notes(self):
        notes = self.notes.limit(50).all()
        return notes

    def get_note(self, note_id):
        return self.notes.filter_by(id=note_id).first()

    def delete_note(self, note_id):
        note = self.notes.filter_by(id=note_id).first()
        if note:
            db.session.delete(note)

    def update_note(self,
                    note_id,
                    title=None,
                    body=None,
                    color=None,
                    tags_string=None):
        return self.notes.filter_by(id=note_id).update({
            Notes.title:
            title,
            Notes.body:
            body,
            Notes.color:
            color,
            Notes.tags_string:
            tags_string
        })

    def move_note_to_trash(self, note):
        if note:
            trashed_note = Trash(user_id=self.id,
                                 title=note.title,
                                 body=note.body,
                                 color=note.color,
                                 tags_string=note.tags_string)
            db.session.add(trashed_note)
            self.delete_note(note.id)
            db.session.commit()
            return trashed_note
        return None

    def restore_from_trash(self, note_id):
        trashed_note = self.get_trashed_note(note_id)
        if trashed_note:
            note = Notes(user_id=self.id,
                         title=trashed_note.title,
                         body=trashed_note.body,
                         color=trashed_note.color,
                         tags_string=trashed_note.tags_string)
            db.session.add(note)
            self.delete_trashed_notes(note_id=note_id)
            db.session.commit()
            return note
        return None

    def get_trashed_notes(self):
        notes = self.trashed_notes.limit(50).all()
        return notes

    def get_trashed_note(self, note_id):
        return self.trashed_notes.filter_by(id=note_id).first()

    def delete_trashed_notes(self, note_id=None, all=False):
        if not note_id and not all:
            return None
        if note_id:
            self.trashed_notes.filter_by(id=note_id).delete()
        if all:
            self.trashed_notes.delete()
        db.session.commit()

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return self.id

    def __repr__(self):
        return '<User %r>' % (self.username)
class Department(db.Model):
    departmentID = db.Column(db.Integer(),
                             primary_key=True,
                             unique=True,
                             nullable=False)
    name = db.Column(db.String(32), nullable=False)
Beispiel #11
0
class Texte(db.Model):
    __tablename__ = 'Texte'
    __bind_key__ = 'plu'

    id_texte = db.Column('id_texte', db.Integer, primary_key=True)
    texte = db.Column('texte', db.String(1000))
Beispiel #12
0
class ExchangeContract(ModelBase):
    """
    class for tracking contracts used for making on-chain exchanges of tokens
    (rather than using an internal sempo blockchain account that holds and swaps multiple tokens)
    currently only supports exchanges using liquid token contracts, though could be extended to support
    a constant-product market maker, continuous-double auction DEX etc.

    @:param blockchain_address:
    The address to which exchange requests should be sent.
    @:param contract_registry_blockchain_address:
    The contract registry is used to add new liquid token sub-exchanges.
    @:param subexchange_address_mapping:
    Exchanges made using a liquid token don't use a single on-chain contract, but rather a network of
    exchange-contracts, one for each token that can be exchanged, which we label 'sub-exchanges'.
    Each one of these sub-exchanges includes an internal reserve-token balance, and has parameters defined
    such as the reserve-ratio.
    @:param reserve_token:
    The stable token used as the reserve for liquid tokens.
    @:param exchangeable_tokens:
    The tokens that are exchangable using this contract
    @:param transfer_accounts:
    Accounts used for tracking the sends and receives of the various tokens exchangable by the exchange-network
    """
    __tablename__ = 'exchange_contract'

    blockchain_address = db.Column(db.String(), index=True)

    contract_registry_blockchain_address = db.Column(db.String(), index=True)

    subexchange_address_mapping = db.Column(JSON)

    reserve_token_id = db.Column(db.Integer, db.ForeignKey("token.id"))

    exchangeable_tokens = db.relationship(
        "Token",
        secondary=exchange_contract_token_association_table,
        back_populates="exchange_contracts")

    transfer_accounts = db.relationship(
        'TransferAccount', backref='exchange_contract',
        lazy=True, foreign_keys='TransferAccount.exchange_contract_id'
    )

    def get_subexchange_details(self, token_address):
        if self.subexchange_address_mapping is None:
            raise SubexchangeNotFound

        details = self.subexchange_address_mapping.get(token_address, None)

        if not details:
            raise SubexchangeNotFound

        return details


    def _add_subexchange(self, token_address, subexchange_address, subexchange_reserve_ratio_ppm):
        if self.subexchange_address_mapping is None:
            self.subexchange_address_mapping = {}
        self.subexchange_address_mapping[token_address] = {
            'subexchange_address': subexchange_address,
            'subexchange_reserve_ratio_ppm': subexchange_reserve_ratio_ppm
        }

        if self.blockchain_address is None:
            self.blockchain_address = subexchange_address

        flag_modified(self, "subexchange_address_mapping")
        db.session.add(self)

    def add_reserve_token(self, reserve_token):
        self.reserve_token = reserve_token
        self.add_token(reserve_token, None, None)

    def add_token(self, token, subexchange_address, subexchange_reserve_ratio):

        exchange_transfer_account = (server.models.transfer_account.TransferAccount.query
                                     .filter_by(token=token)
                                     .filter_by(exchange_contract=self)
                                     .first())

        if not exchange_transfer_account:

            exchange_transfer_account = server.models.transfer_account.TransferAccount(
                bound_entity=self,
                token=token,
                is_approved=True)

            db.session.add(exchange_transfer_account)

        if subexchange_address:
            self.exchangeable_tokens.append(token)
            self._add_subexchange(token.address, subexchange_address, subexchange_reserve_ratio)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
Beispiel #13
0
class User(db.Model):
    id = db.Column(db.String, primary_key=True, default=uuid.uuid4().hex)
    username = db.Column(db.String(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(60), nullable=False)
    bio = db.Column(db.String(150))
    image_file = db.Column(db.String(20),default='default.jpg')
    influence = db.Column(db.Integer, default=0)
    date = db.Column(db.String(10), nullable=False, default=datetime.utcnow)
    user_post = db.relationship("Post", backref="parent_post_user", lazy=True)
    user_comment = db.relationship("Comment", backref="parent_comment_user", lazy=True)
    user_postlike = db.relationship("PostLike", backref="parent_postlike_user", lazy=True)
    user_postdislike = db.relationship("PostDislike", backref="parent_postdislike_user", lazy=True)
    user_commentlike = db.relationship("CommentLike", backref="parent_commentlike_user", lazy=True)
    user_commentdislike = db.relationship("CommentDislike", backref="parent_commentdislike_user", lazy=True)
    
    def get_reset_token(self, expires_sec=1800):
        s = Serializer(current_app.config['SECRET_KEY'], expires_sec)
        return s.dumps({'user_id':self.id}).decode('utf-8')
   
    @staticmethod
    def verify_reset_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            user_id = s.loads(token)['user_id']
        except:
            return None
        return User.query.get(user_id)

    def like_post(self, model, post):
        if not self.has_liked_post(model, post):
            like = model(username=self.username, post_id=post.id)
            db.session.add(like)

    def unlike_post(self, model,post):
        if self.has_liked_post(model, post):
            model.query.filter_by(
                username=self.username,
                post_id=post.id).delete()

    def has_liked_post(self, model, post):
        return model.query.filter(
            model.username == self.username,
            model.post_id == post.id).count() > 0

    def like_comment(self, model, comment):
        if not self.has_liked_comment(model, comment):
            like = model(username=self.username, comment_id=comment.id)
            db.session.add(like)

    def unlike_comment(self, model,comment):
        if self.has_liked_comment(model, comment):
            model.query.filter_by(
                username=self.username,
                comment_id=comment.id).delete()

    def has_liked_comment(self, model, comment):
        return model.query.filter(
            model.username == self.username,
            model.comment_id == comment.id).count() > 0

    def likes_recieved(self):
        posts = [z.to_json() for z in self.user_post]
        counter = 0
        for post in posts:
            counter +=  post['likes']
        return counter
    def to_json(self):
        return {
            "username": self.username,
            "email": self.email,
            "influence": self.influence,
            "date": self.date,
            "user_post": [z.to_json() for z in self.user_post],
            "user_comment": [z.to_json() for z in self.user_comment],
            "likes_recieved": self.likes_recieved(),
            "bio": self.bio
        }
    def user_post_to_json(self):
        return {"user_post" : [z.to_json() for z in self.user_post]}

    def __repr__(self):
        return f"User('{self.username}','{self.email}','{self.image_file}','{self.influence}', '{self.user_post}')"
Beispiel #14
0
class Asesi(db.Model):
    __tablename__ = 'Asesi'
    idAsesi = db.Column(db.Integer, primary_key=True, autoincrement=True)
    NamaLengkap = db.Column(db.String(64), nullable=False)
    TempatLahir = db.Column(db.String(255), nullable=False)
    JenisKelamin = db.Column(db.String(1), nullable=False)
    Kebangsaan = db.Column(db.String(64), nullable=False)
    Alamat = db.Column(db.String(255), nullable=False)
    KodePos = db.Column(db.String(8), nullable=False)
    NoHP = db.Column(db.String(13), nullable=False)
    NoHPRumah = db.Column(db.String(13))
    NoHPKantor = db.Column(db.String(13))
    Email = db.Column(db.String(64))
    PendidikanTerakhir = db.Column(db.String(128))
    StatusBekerja = db.Column(db.Boolean())
    NamaPerusahaan = db.Column(db.String(128), nullable=False)
    Jabatan = db.Column(db.String(128))
    AlamatPerusahaan = db.Column(db.String(255))
    KodePosPerusahaan = db.Column(db.String(8))
    NoTeleponPerusahaan = db.Column(db.String(13))
    NoFaxPerusahaan = db.Column(db.String(32))
    EmailPerusahaan = db.Column(db.String(64))
    Skema = db.Column(db.Integer, db.ForeignKey('Skema.idSkema'))

    def __init__(self, nama, tempat, kelamin, bangsa, alamat, pos, nohp, rumah,
                 kantor, email, pendidikan, statuskerja, pekerjaan, Skema):
        self.NamaLengkap = nama
        self.TempatLahir = tempat
        self.JenisKelamin = kelamin
        self.Kebangsaan = bangsa
        self.alamat = alamat
        self.KodePos = pos
        self.NoHP = nohp
        self.NoHPRumah = rumah
        self.NoHPKantor = kantor
        self.Email = email
        self.PendidikanTerakhir = pendidikan
        self.StatusKompeten = False
        self.StatusBekerja = statuskerja
        self.bekerja = pekerjaan
        self.Skema = Skema

    def __repr__(self):
        return self.NamaLengkap

    def cekKompeten(self):
        return self.StatusKompeten

    def setDataPerusahaan(self, nama, jabatan, alamat, pos, telepon, fax,
                          email):
        self.NamaPerusahaan = nama
        self.Jabatan = jabatan
        self.AlamatPerusahaan = alamat
        self.KodePosPerusahaan = pos
        self.NoTeleponPerusahaan = telepon
        self.NoFaxPerusahaan = fax
        self.EmailPerusahaan = email
Beispiel #15
0
class Account(BaseModel, db.Model):
    """
        Model for the account table
    """
    __tablename__ = 'account'

    # The account id
    id = db.Column(UUID(as_uuid=True), server_default=db.text("uuid_generate_v4()"), unique=True, primary_key=True)
    # The user id 									
    user_id = db.Column(db.String(255), unique=True, nullable=False)
    # The password
    password = db.Column(db.String(255), nullable=False)
    # The ammount in his account
    balance = db.Column(db.Float, default=0.0, nullable=False)
    # The atual currency
    currency = db.Column(db.Enum(Currency), default=Currency.eur, nullable=False) 
    # The state of the account : active or inactive
    state = db.Column(db.Boolean, default=True, nullable=False)
    # The date when the account was created
    created_at = db.Column(db.DateTime, nullable=False)
    # The date when the information account was updated
    updated_at = db.Column(db.DateTime, nullable=False)

    def __init__(self, user_id, password, currency):
        self.user_id = user_id
        self.password = bcrypt.generate_password_hash(password, app.config['BCRYPT_LOG_ROUNDS']).decode()
        self.balance = 0.0
        self.currency = currency
        self.state = True
        self.created_at = self.updated_at = datetime.datetime.utcnow().isoformat()

    def __repr__(self):
        return '<user_id = '+str(self.user_id)+', password = '******'>'

    @classmethod
    def find_by_id(cls, user_id):
        return cls.query.filter_by(user_id=user_id).first()

    def save_to_db(self):
        db.session.add(self)
        db.session.commit()

    @staticmethod
    def encode_auth_token(user_id):
        """
            Generates the Auth Token
            :return: string
        """
        try:
            payload = {
                'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1, seconds=3600),
                'iat': datetime.datetime.utcnow(),
                'sub': str(user_id)
            }
            return jwt.encode(payload, app.config['SECRET_KEY'], algorithm='HS256')
        except Exception as e:
            return e
    
    @staticmethod
    def decode_auth_token(auth_token):
        """
            Validates the auth token
            :param auth_token:
            :return: integer|string
        """
        try:
            payload = jwt.decode(auth_token, app.config['SECRET_KEY'])
            is_active_token = Active_Sessions.check_active_session(auth_token)
            if not is_active_token:
                return 'Token invalid.'
            else:
                return payload['sub'] 
        except jwt.ExpiredSignatureError:
            return 'Signature expired. Please log in again.'
        except jwt.InvalidTokenError:
            return 'Invalid token. Please log in again.'

    def check_password_hash(hash, password):
        return bcrypt.check_password_hash(hash, password)

    def save_to_db(self):
        db.session.add(self)
        db.session.commit()
Beispiel #16
0
class CreditTransfer(ManyOrgBase, BlockchainTaskableBase):
    __tablename__ = 'credit_transfer'

    uuid = db.Column(db.String, unique=True)
    batch_uuid = db.Column(db.String)

    # override ModelBase deleted to add an index
    created = db.Column(db.DateTime,
                        default=datetime.datetime.utcnow,
                        index=True)

    resolved_date = db.Column(db.DateTime)
    _transfer_amount_wei = db.Column(db.Numeric(27), default=0)

    transfer_type = db.Column(db.Enum(TransferTypeEnum), index=True)
    transfer_subtype = db.Column(db.Enum(TransferSubTypeEnum))
    transfer_status = db.Column(db.Enum(TransferStatusEnum),
                                default=TransferStatusEnum.PENDING)
    transfer_mode = db.Column(db.Enum(TransferModeEnum), index=True)
    transfer_use = db.Column(JSON)  # Deprecated
    transfer_usages = db.relationship(
        "TransferUsage",
        secondary=credit_transfer_transfer_usage_association_table,
        back_populates="credit_transfers",
        lazy='joined')
    transfer_metadata = db.Column(JSONB)

    exclude_from_limit_calcs = db.Column(db.Boolean, default=False)

    resolution_message = db.Column(db.String())

    token_id = db.Column(db.Integer, db.ForeignKey(Token.id))

    sender_transfer_account_id = db.Column(
        db.Integer, db.ForeignKey("transfer_account.id"), index=True)
    sender_transfer_account = db.relationship(
        'TransferAccount',
        foreign_keys=[sender_transfer_account_id],
        back_populates='credit_sends',
        lazy='joined')

    recipient_transfer_account_id = db.Column(
        db.Integer, db.ForeignKey("transfer_account.id"), index=True)
    recipient_transfer_account = db.relationship(
        'TransferAccount',
        foreign_keys=[recipient_transfer_account_id],
        back_populates='credit_receives',
        lazy='joined')

    sender_blockchain_address_id = db.Column(
        db.Integer, db.ForeignKey("blockchain_address.id"), index=True)
    recipient_blockchain_address_id = db.Column(
        db.Integer, db.ForeignKey("blockchain_address.id"), index=True)

    sender_transfer_card_id = db.Column(db.Integer,
                                        db.ForeignKey("transfer_card.id"),
                                        index=True)

    sender_user_id = db.Column(db.Integer,
                               db.ForeignKey("user.id"),
                               index=True)
    recipient_user_id = db.Column(db.Integer,
                                  db.ForeignKey("user.id"),
                                  index=True)

    is_initial_disbursement = db.Column(db.Boolean, default=False)

    attached_images = db.relationship('UploadedResource',
                                      backref='credit_transfer',
                                      lazy='joined')

    fiat_ramp = db.relationship('FiatRamp',
                                backref='credit_transfer',
                                lazy=True,
                                uselist=False)

    __table_args__ = (Index('updated_index', "updated"), )

    from_exchange = db.relationship('Exchange',
                                    backref='from_transfer',
                                    lazy='joined',
                                    uselist=False,
                                    foreign_keys='Exchange.from_transfer_id')

    to_exchange = db.relationship('Exchange',
                                  backref='to_transfer',
                                  lazy=True,
                                  uselist=False,
                                  foreign_keys='Exchange.to_transfer_id')

    def add_message(self, message):
        dated_message = f"[{datetime.datetime.utcnow()}:: {message}]"
        self.resolution_message = dated_message

    # TODO: Apply this to all transfer amounts/balances, work out the correct denominator size
    @hybrid_property
    def transfer_amount(self):
        return (self._transfer_amount_wei or 0) / int(1e16)

    @transfer_amount.setter
    def transfer_amount(self, val):
        self._transfer_amount_wei = val * int(1e16)

    @hybrid_property
    def rounded_transfer_amount(self):
        return (self._transfer_amount_wei or 0) / int(1e18)

    @hybrid_property
    def public_transfer_type(self):
        if self.transfer_type == TransferTypeEnum.PAYMENT:
            if self.transfer_subtype == TransferSubTypeEnum.STANDARD or None:
                return TransferTypeEnum.PAYMENT
            else:
                return self.transfer_subtype
        else:
            return self.transfer_type

    @public_transfer_type.expression
    def public_transfer_type(cls):
        from sqlalchemy import case, cast, String
        return case([
            (cls.transfer_subtype
             == TransferSubTypeEnum.STANDARD, cast(cls.transfer_type, String)),
            (cls.transfer_type
             == TransferTypeEnum.PAYMENT, cast(cls.transfer_subtype, String)),
        ],
                    else_=cast(cls.transfer_type, String))

    def send_blockchain_payload_to_worker(self,
                                          is_retry=False,
                                          queue='high-priority'):
        sender_approval = self.sender_transfer_account.get_or_create_system_transfer_approval(
        )
        recipient_approval = self.recipient_transfer_account.get_or_create_system_transfer_approval(
        )

        # Approval is called so that the master account can make transactions on behalf of the transfer account.
        # Make sure this approval is done first before making a transaction
        approval_priors = list(
            filter(lambda x: x is not None, [
                sender_approval.eth_send_task_uuid,
                sender_approval.approval_task_uuid,
                recipient_approval.eth_send_task_uuid,
                recipient_approval.approval_task_uuid
            ]))

        # Forces an order on transactions so that if there's an outage somewhere, transactions don't get confirmed
        # On chain in an order that leads to a unrecoverable state
        other_priors = [
            t.blockchain_task_uuid for t in self._get_required_prior_tasks()
        ]

        all_priors = approval_priors + other_priors

        return bt.make_token_transfer(
            signing_address=self.sender_transfer_account.organisation.
            system_blockchain_address,
            token=self.token,
            from_address=self.sender_transfer_account.blockchain_address,
            to_address=self.recipient_transfer_account.blockchain_address,
            amount=self.transfer_amount,
            prior_tasks=all_priors,
            queue=queue,
            task_uuid=self.blockchain_task_uuid)

    def _get_required_prior_tasks(self):
        """
        Get the tasks involving the sender's account that must complete prior to this task being submitted to chain
        To calculate the prior tasks for the sender Alice:

        - Find the most recent credit transfer where Alice was the sender, not including any transfers that have the
            same batch UUID as this transfer. Call this "most_recent_out_of_batch_send"
        - Find all credit transfers subsequent to "most_recent_out_of_batch_send" where Alice was the recipient. Call
            this "more_recent_receives"

        Required priors are all transfers in "more_recent_receives" and "most_recent_out_of_batch_send".
        For why this works, see https://github.com/teamsempo/SempoBlockchain/pull/262

        """

        # We're constantly querying complete transfers here. Lazy and DRY
        complete_transfer_base_query = (CreditTransfer.query.filter(
            CreditTransfer.transfer_status == TransferStatusEnum.COMPLETE))

        # Query for finding the most recent transfer sent by the sending account that isn't from the same batch uuid
        # that of the transfer in question
        most_recent_out_of_batch_send = (
            complete_transfer_base_query.order_by(
                CreditTransfer.id.desc()).filter(
                    CreditTransfer.sender_transfer_account ==
                    self.sender_transfer_account).
            filter(CreditTransfer.id != self.id).filter(
                or_(
                    CreditTransfer.batch_uuid != self.batch_uuid,
                    CreditTransfer.batch_uuid ==
                    None  # Only exclude matching batch_uuids if they're not null
                )).first())

        # Base query for finding more_recent_receives
        base_receives_query = (complete_transfer_base_query.filter(
            CreditTransfer.recipient_transfer_account ==
            self.sender_transfer_account))

        if most_recent_out_of_batch_send:
            # If most_recent_out_of_batch_send exists, find all receive transfers since it.
            more_recent_receives = base_receives_query.filter(
                CreditTransfer.id > most_recent_out_of_batch_send.id).all()

            # Required priors are then the out of batch send plus these receive transfers
            required_priors = more_recent_receives + [
                most_recent_out_of_batch_send
            ]

            # Edge case handle: if most_recent_out_of_batch_send is a batch member, the whole batch are priors as well
            if most_recent_out_of_batch_send.batch_uuid is not None:
                same_batch_priors = complete_transfer_base_query.filter(
                    CreditTransfer.batch_uuid ==
                    most_recent_out_of_batch_send.batch_uuid).all()

                required_priors = required_priors + same_batch_priors

        else:
            # Otherwise, return all receives, which are all our required priors
            required_priors = base_receives_query.all()

        # Filter out any transfers that we already know are complete - there's no reason to create an extra dep
        # We don't do this inside the Alchemy queries because we need the completed priors to calculate other priors
        required_priors = [
            prior for prior in required_priors
            if prior.blockchain_status != BlockchainStatus.SUCCESS
        ]

        # Remove any possible duplicates
        return set(required_priors)

    def resolve_as_complete_with_existing_blockchain_transaction(
            self, transaction_hash):

        self.resolve_as_complete()

        self.blockchain_status = BlockchainStatus.SUCCESS
        self.blockchain_hash = transaction_hash

    def resolve_as_complete_and_trigger_blockchain(
            self,
            existing_blockchain_txn=None,
            queue='high-priority',
            batch_uuid: str = None):

        self.resolve_as_complete(batch_uuid)

        if not existing_blockchain_txn:
            self.blockchain_task_uuid = str(uuid4())
            g.pending_transactions.append((self, queue))

    def resolve_as_complete(self, batch_uuid=None):
        if self.transfer_status not in [None, TransferStatusEnum.PENDING]:
            raise Exception(
                f'Resolve called multiple times for transfer {self.id}')
        try:
            self.check_sender_transfer_limits()
        except TransferLimitError as e:
            # Sempo admins can always bypass limits, allowing for things like emergency moving of funds etc
            if hasattr(g, 'user') and AccessControl.has_suffient_role(
                    g.user.roles, {'ADMIN': 'sempoadmin'}):
                self.add_message(f'Warning: {e}')
            else:
                raise e

        self.resolved_date = datetime.datetime.utcnow()
        self.transfer_status = TransferStatusEnum.COMPLETE
        self.update_balances()

        if self.transfer_type == TransferTypeEnum.PAYMENT and self.transfer_subtype == TransferSubTypeEnum.DISBURSEMENT:
            if self.recipient_user and self.recipient_user.transfer_card:
                self.recipient_user.transfer_card.update_transfer_card()

        if batch_uuid:
            self.batch_uuid = batch_uuid

        if self.fiat_ramp and self.transfer_type in [
                TransferTypeEnum.DEPOSIT, TransferTypeEnum.WITHDRAWAL
        ]:
            self.fiat_ramp.resolve_as_complete()

    def resolve_as_rejected(self, message=None):
        if self.transfer_status not in [None, TransferStatusEnum.PENDING]:
            raise Exception(
                f'Resolve called multiple times for transfer {self.id}')

        if self.fiat_ramp and self.transfer_type in [
                TransferTypeEnum.DEPOSIT, TransferTypeEnum.WITHDRAWAL
        ]:
            self.fiat_ramp.resolve_as_rejected()

        self.resolved_date = datetime.datetime.utcnow()
        self.transfer_status = TransferStatusEnum.REJECTED
        self.blockchain_status = BlockchainStatus.UNSTARTED
        self.update_balances()

        if message:
            self.add_message(message)

    def update_balances(self):
        self.sender_transfer_account.update_balance()
        self.recipient_transfer_account.update_balance()

    def get_transfer_limits(self):
        from server.utils.transfer_limits import (
            LIMIT_IMPLEMENTATIONS, get_applicable_transfer_limits)

        return get_applicable_transfer_limits(LIMIT_IMPLEMENTATIONS, self)

    def check_sender_transfer_limits(self):
        if self.sender_user is None:
            # skip if there is no sender, which implies system send
            return

        relevant_transfer_limits = self.get_transfer_limits()
        for limit in relevant_transfer_limits:

            try:
                limit.validate_transfer(self)
            except (TransferAmountLimitError, TransferCountLimitError,
                    TransferBalanceFractionLimitError,
                    MaximumPerTransferLimitError, MinimumSentLimitError,
                    NoTransferAllowedLimitError) as e:
                self.resolve_as_rejected(message=e.message)
                raise e

        return relevant_transfer_limits

    def check_sender_has_sufficient_balance(self):
        return self.sender_transfer_account.unrounded_balance - Decimal(
            self.transfer_amount) >= 0

    def check_sender_is_approved(self):
        return self.sender_user and self.sender_transfer_account.is_approved

    def check_recipient_is_approved(self):
        return self.recipient_user and self.recipient_transfer_account.is_approved

    def _select_transfer_account(self, token, user):
        if token is None:
            raise Exception("Token must be specified")
        return find_transfer_accounts_with_matching_token(user, token)

    def append_organisation_if_required(self, organisation):
        if organisation and organisation not in self.organisations:
            self.organisations.append(organisation)

    def __init__(self,
                 amount,
                 token=None,
                 sender_user=None,
                 recipient_user=None,
                 sender_transfer_account=None,
                 recipient_transfer_account=None,
                 transfer_type: TransferTypeEnum = None,
                 uuid=None,
                 transfer_metadata=None,
                 fiat_ramp=None,
                 transfer_subtype: TransferSubTypeEnum = None,
                 transfer_mode: TransferModeEnum = None,
                 transfer_card=None,
                 is_ghost_transfer=False,
                 require_sufficient_balance=True):

        if amount < 0:
            raise Exception("Negative amount provided")
        self.transfer_amount = amount

        self.sender_user = sender_user
        self.recipient_user = recipient_user

        self.sender_transfer_account = sender_transfer_account or self._select_transfer_account(
            token, sender_user)

        self.token = token or self.sender_transfer_account.token

        self.fiat_ramp = fiat_ramp

        if transfer_type is TransferTypeEnum.DEPOSIT:
            self.sender_transfer_account = self.recipient_transfer_account.token.float_account

        if transfer_type is TransferTypeEnum.WITHDRAWAL:
            self.recipient_transfer_account = self.sender_transfer_account.token.float_account

        try:
            self.recipient_transfer_account = recipient_transfer_account or self.recipient_transfer_account or self._select_transfer_account(
                self.token, recipient_user)

            if is_ghost_transfer is False:
                self.recipient_transfer_account.is_ghost = False
        except NoTransferAccountError:
            self.recipient_transfer_account = TransferAccount(
                bound_entity=recipient_user,
                token=token,
                is_approved=True,
                is_ghost=is_ghost_transfer)
            db.session.add(self.recipient_transfer_account)

        if self.sender_transfer_account.token != self.recipient_transfer_account.token:
            raise Exception("Tokens do not match")

        self.transfer_type = transfer_type
        self.transfer_subtype = transfer_subtype
        self.transfer_mode = transfer_mode
        self.transfer_metadata = transfer_metadata
        self.transfer_card = transfer_card

        if uuid is not None:
            self.uuid = uuid

        self.append_organisation_if_required(
            self.recipient_transfer_account.organisation)
        self.append_organisation_if_required(
            self.sender_transfer_account.organisation)

        if require_sufficient_balance and not self.check_sender_has_sufficient_balance(
        ):
            message = "Sender {} has insufficient balance. Has {}, needs {}.".format(
                self.sender_transfer_account,
                self.sender_transfer_account.balance, self.transfer_amount)
            self.resolve_as_rejected(message)
            raise InsufficientBalanceError(message)

        self.update_balances()
Beispiel #17
0
class House(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    lianjiaId = db.Column(db.String(20), index=True)
    title = db.Column(db.String(50), index=True)
    follower_num = db.Column(db.Integer, index=True)
    total_price = db.Column(db.Integer, index=True)
    area = db.Column(db.Float, index=True)
    avg_price = db.Column(db.Integer, index=True)
    directions = db.Column(db.String(20), index=True)
    floor = db.Column(db.String(4), index=True)
    year = db.Column(db.Integer, index=True)
    decoration = db.Column(db.String(20), index=True)
    location = db.Column(db.String(300), index=True)
    school = db.Column(db.Integer, index=True)
    mall = db.Column(db.Integer, index=True)
    traffic = db.Column(db.Integer, index=True)
    medicine = db.Column(db.Integer, index=True)
    total_floor = db.Column(db.Integer, index=True)
    ring = db.Column(db.Integer, index=True)
    room = db.Column(db.Integer, index=True)
    parlor = db.Column(db.Integer, index=True)
    kitchen = db.Column(db.Integer, index=True)
    toilet = db.Column(db.Integer, index=True)
    district = db.Column(db.String(40), index=True)
    block = db.Column(db.String(40), index=True)
    name = db.Column(db.String(50), index=True)
    address = db.Column(db.String(50), index=True)
    des = db.Column(db.String(1000), index=True)
    community = db.Column(db.String(100), index=True)

    def __repr__(self):
        return '<House {}>'.format(self.lianjiaId)

    # 查询单条数据
    def to_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

    # 查询多条数据
    def dobule_to_dict(self):
        result = {}
        for key in self.__mapper__.c.keys():
            if getattr(self, key) is not None:
                result[key] = str(getattr(self, key))
            else:
                result[key] = getattr(self, key)
        return result

    #配合todict一起使用
    def to_json(all_vendors):
        v = [ven.dobule_to_dict() for ven in all_vendors]
        return v

    def to_json(self):
        dict = self.__dict__
        if "_sa_instance_state" in dict:
            del dict["_sa_instance_state"]
        return dict
Beispiel #18
0
class Users(db.Model):
    email = db.Column(db.String(120), primary_key=True)
    first_name = db.Column(db.String(120))
    last_name = db.Column(db.String(120))
    organization = db.Column(db.String(120), nullable=True)
    descr = db.Column(db.String(250), nullable=True)
    user_type = db.Column(db.String(120), nullable=True)
    gen_link_1 = db.Column(db.String(250), nullable=True)
    gen_link_2 = db.Column(db.String(250), nullable=True)
    gen_link_3 = db.Column(db.String(250), nullable=True)
    image = db.Column(db.String(250))
    doc = db.Column(db.String(250), nullable=True)

    def __init__(self, e, fname, lname, org, des, u_type, gl1, gl2, gl3, img,
                 d):
        self.email = e
        self.first_name = fname
        self.last_name = lname
        self.organization = org
        self.descr = des
        self.user_type = u_type
        self.gen_link_1 = gl1
        self.gen_link_2 = gl2
        self.gen_link_3 = gl3
        self.image = img
        self.doc = d
Beispiel #19
0
class Fuec(db.Model):
    __table_args__ = {'extend_existing': True}
    __tablename__ = 'fuec'
    id = db.Column(db.Integer, primary_key=True)
    created_at = db.Column(db.DateTime, default=db.func.current_timestamp())
    created_by = db.Column(db.Integer, db.ForeignKey('user.id'))
    no_fuec = db.Column(db.String(20))
    social_object = db.Column(db.String(255), nullable=False)
    nit = db.Column(db.String(255))
    no_agreement = db.Column(db.Integer)
    contractor = db.Column(db.String(255))
    id_contractor = db.Column(db.Integer)
    object_agreement = db.Column(db.String(255))
    origin_destination = db.Column(db.String(1000))
    kind_hiring = db.Column(db.String(255))
    kind_link = db.Column(db.String(255))

    init_date = db.Column(db.String(255))
    last_date = db.Column(db.String(255))

    car_no = db.Column(db.Integer)
    car_license_plate = db.Column(db.String(255))
    car_model = db.Column(db.String(255))
    car_brand = db.Column(db.String(255))
    car_class_car = db.Column(db.Integer)
    car_operation = db.Column(db.String(255))

    data_driver_json = db.Column(db.String(1000))
    contractor_owner = db.Column(db.String(1000))

    file_pdf = db.Column(db.LargeBinary)

    def __init__(self, no_fuec, created_by, social_object, nit, no_agreement,
                 contractor, id_contractor, object_agreement,
                 origin_destination, kind_hiring, kind_link, init_date,
                 last_date, car_no, car_license_plate, car_model, car_brand,
                 car_class_car, car_operation, data_driver, contractor_owner,
                 file_pdf):

        if no_fuec:
            self.no_fuec = no_fuec
        if created_by:
            self.created_by = created_by
        if social_object:
            self.social_object = social_object.lower()
        if nit:
            self.nit = nit
        if no_agreement:
            self.no_agreement = no_agreement
        if contractor:
            self.contractor = contractor
        if id_contractor:
            self.id_contractor = id_contractor
        if object_agreement:
            self.object_agreement = object_agreement
        if origin_destination:
            self.origin_destination = origin_destination
        if kind_hiring:
            self.kind_hiring = kind_hiring
        if kind_link:
            self.kind_link = kind_link
        if init_date:
            self.init_date = init_date
        if last_date:
            self.last_date = last_date
        if car_no:
            self.car_no = car_no
        if car_license_plate:
            self.car_license_plate = car_license_plate
        if car_model:
            self.car_model = car_model
        if car_brand:
            self.car_brand = car_brand
        if car_class_car:
            self.car_class_car = car_class_car
        if car_operation:
            self.car_operation = car_operation
        if data_driver:
            self.data_driver_json = data_driver
        if contractor_owner:
            self.contractor_owner = contractor_owner
        if file_pdf:
            self.file_pdf = file_pdf
Beispiel #20
0
class Data(db.Model):
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    text = db.Column(db.String(500))
Beispiel #21
0
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(30), nullable=False)
    password = db.Column(db.String(10), nullable=False)
    country = db.Column(db.String(10), nullable=True)
    gender = db.Column(db.String(1), nullable=False)
Beispiel #22
0
class User(db.Model):
    id = db.Column('user_id', db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)

    def __init__(self, name):
        self.name = name
Beispiel #23
0
class Login(db.Model):
    username = db.Column(db.String(255), primary_key=True)
    password = db.Column(db.String(255))
Beispiel #24
0
class Genre(db.Model):
    __tablename__ = 'Genre'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(), nullable=False)
class Course(db.Model):

    __tablename__ = 'COURSES'
    id = db.Column(db.Integer, primary_key=True, nullable=False)
    name = db.Column(db.String(10), nullable=False)
    session = db.Column(db.String(5), nullable=False)
Beispiel #26
0
class User(ManyOrgBase, ModelBase, SoftDelete):
    """Establishes the identity of a user for both making transactions and more general interactions.

        Admin users are created through the auth api by registering
        an account with an email that has been pre-approved on the whitelist.
        By default, admin users only have minimal access levels (view).
        Permissions must be elevated manually in the database.

        Transaction-capable users (vendors and beneficiaries) are
        created using the POST user API or the bulk upload function
    """
    __tablename__ = 'user'

    first_name = db.Column(db.String())
    last_name = db.Column(db.String())
    preferred_language = db.Column(db.String())

    primary_blockchain_address = db.Column(db.String())

    _last_seen = db.Column(db.DateTime)

    email = db.Column(db.String())
    _phone = db.Column(db.String(), unique=True, index=True)
    _public_serial_number = db.Column(db.String())
    uuid = db.Column(db.String(), index=True)

    nfc_serial_number = db.Column(db.String())

    password_hash = db.Column(db.String(200))
    one_time_code = db.Column(db.String)
    secret = db.Column(db.String())
    _TFA_secret = db.Column(db.String(128))
    TFA_enabled = db.Column(db.Boolean, default=False)
    pin_hash = db.Column(db.String())
    seen_latest_terms = db.Column(db.Boolean, default=False)

    failed_pin_attempts = db.Column(db.Integer, default=0)

    default_currency = db.Column(db.String())

    _location = db.Column(db.String())
    lat = db.Column(db.Float())
    lng = db.Column(db.Float())

    _held_roles = db.Column(JSONB)

    is_activated = db.Column(db.Boolean, default=False)
    is_disabled = db.Column(db.Boolean, default=False)
    is_phone_verified = db.Column(db.Boolean, default=False)
    is_self_sign_up = db.Column(db.Boolean, default=True)
    is_market_enabled = db.Column(db.Boolean, default=False)

    password_reset_tokens = db.Column(JSONB, default=[])
    pin_reset_tokens = db.Column(JSONB, default=[])

    terms_accepted = db.Column(db.Boolean, default=True)

    matched_profile_pictures = db.Column(JSON)

    business_usage_id = db.Column(db.Integer, db.ForeignKey(TransferUsage.id))

    transfer_accounts = db.relationship(
        "TransferAccount",
        secondary=user_transfer_account_association_table,
        back_populates="users")

    default_transfer_account_id = db.Column(
        db.Integer, db.ForeignKey('transfer_account.id'))

    default_transfer_account = db.relationship(
        'TransferAccount',
        primaryjoin='TransferAccount.id == User.default_transfer_account_id',
        lazy=True,
        uselist=False)

    default_organisation_id = db.Column(db.Integer,
                                        db.ForeignKey('organisation.id'))

    default_organisation = db.relationship(
        'Organisation',
        primaryjoin=Organisation.id == default_organisation_id,
        lazy=True,
        uselist=False)

    # roles = db.relationship('UserRole', backref='user', lazy=True,
    #                              foreign_keys='UserRole.user_id')

    ussd_sessions = db.relationship('UssdSession',
                                    backref='user',
                                    lazy=True,
                                    foreign_keys='UssdSession.user_id')

    uploaded_images = db.relationship('UploadedResource',
                                      backref='user',
                                      lazy=True,
                                      foreign_keys='UploadedResource.user_id')

    kyc_applications = db.relationship('KycApplication',
                                       backref='user',
                                       lazy=True,
                                       foreign_keys='KycApplication.user_id')

    devices = db.relationship('DeviceInfo', backref='user', lazy=True)

    referrals = db.relationship(
        'User',
        secondary=referrals,
        primaryjoin="User.id == referrals.c.referred_user_id",
        secondaryjoin="User.id == referrals.c.referrer_user_id",
        backref='referred_by')

    transfer_card = db.relationship('TransferCard',
                                    backref='user',
                                    lazy=True,
                                    uselist=False)

    credit_sends = db.relationship(
        'CreditTransfer',
        backref='sender_user',
        lazy='dynamic',
        foreign_keys='CreditTransfer.sender_user_id')

    credit_receives = db.relationship(
        'CreditTransfer',
        backref='recipient_user',
        lazy='dynamic',
        foreign_keys='CreditTransfer.recipient_user_id')

    ip_addresses = db.relationship('IpAddress', backref='user', lazy=True)

    feedback = db.relationship('Feedback',
                               backref='user',
                               lazy='dynamic',
                               foreign_keys='Feedback.user_id')

    custom_attributes = db.relationship(
        "CustomAttributeUserStorage",
        backref='user',
        lazy='joined',
        foreign_keys='CustomAttributeUserStorage.user_id')

    exchanges = db.relationship("Exchange", backref="user")

    def delete_user_and_transfer_account(self):
        """
        Soft deletes a User and default Transfer account if no other users associated to it.
        Removes User PII
        """
        try:
            ta = self.default_transfer_account
            ta.delete_transfer_account_from_user(user=self)

            timenow = datetime.datetime.utcnow()
            self.deleted = timenow

            self.first_name = None
            self.last_name = None
            self.phone = None

        except (ResourceAlreadyDeletedError,
                TransferAccountDeletionError) as e:
            raise e

    @hybrid_property
    def cashout_authorised(self):
        # loop over all
        any_valid_token = [t.token for t in self.transfer_accounts]
        for token in any_valid_token:
            ct = server.models.credit_transfer
            example_transfer = ct.CreditTransfer(
                transfer_type=ct.TransferTypeEnum.PAYMENT,
                transfer_subtype=ct.TransferSubTypeEnum.AGENT_OUT,
                sender_user=self,
                recipient_user=self,
                token=token,
                amount=0)

            limits = example_transfer.get_transfer_limits()
            limit = limits[0]
            return limit.period_amount > 0
        else:
            # default to false
            return False

    @hybrid_property
    def phone(self):
        return self._phone

    @phone.setter
    def phone(self, phone):
        self._phone = proccess_phone_number(phone)

    @hybrid_property
    def public_serial_number(self):
        return self._public_serial_number

    @public_serial_number.setter
    def public_serial_number(self, public_serial_number):
        self._public_serial_number = public_serial_number

        try:
            transfer_card = TransferCard.get_transfer_card(
                public_serial_number)

            if transfer_card.user_id is None and transfer_card.nfc_serial_number is not None:
                # Card hasn't been used before, and has a nfc number attached
                self.nfc_serial_number = transfer_card.nfc_serial_number
                self.transfer_card = transfer_card

        except NoTransferCardError:
            pass

    @hybrid_property
    def tfa_url(self):

        if not self._TFA_secret:
            self.set_TFA_secret()
            db.session.flush()

        secret_key = self.get_TFA_secret()
        return pyotp.totp.TOTP(secret_key).provisioning_uri(
            self.email,
            issuer_name='Sempo: {}'.format(
                current_app.config.get('DEPLOYMENT_NAME')))

    @hybrid_property
    def location(self):
        return self._location

    @location.setter
    def location(self, location):

        self._location = location

    def attempt_update_gps_location(self):
        from server.utils.location import async_set_user_gps_from_location

        if self._location is not None and self._location is not '':
            # Delay execution until after request to avoid race condition with db
            # We still need to flush to get user id though
            db.session.flush()
            add_after_request_executor_job(async_set_user_gps_from_location,
                                           kwargs={
                                               'user_id': self.id,
                                               'location': self._location
                                           })

    @hybrid_property
    def roles(self):
        if self._held_roles is None:
            return {}
        return self._held_roles

    def remove_all_held_roles(self):
        self._held_roles = {}

    def set_held_role(self, role: str, tier: Union[str, None]):
        if role not in ACCESS_ROLES:
            raise RoleNotFoundException("Role '{}' not valid".format(role))
        allowed_tiers = ACCESS_ROLES[role]
        if tier is not None and tier not in allowed_tiers:
            raise TierNotFoundException(
                "Tier {} not recognised for role {}".format(tier, role))

        if self._held_roles is None:
            self._held_roles = {}
        if tier is None:
            self._held_roles.pop(role, None)
        else:
            self._held_roles[role] = tier
            flag_modified(self, '_held_roles')

    @hybrid_property
    def has_admin_role(self):
        return AccessControl.has_any_tier(self.roles, 'ADMIN')

    @has_admin_role.expression
    def has_admin_role(cls):
        return cls._held_roles.has_key('ADMIN')

    @hybrid_property
    def has_vendor_role(self):
        return AccessControl.has_any_tier(self.roles, 'VENDOR')

    @has_vendor_role.expression
    def has_vendor_role(cls):
        return cls._held_roles.has_key('VENDOR')

    @hybrid_property
    def has_beneficiary_role(self):
        return AccessControl.has_any_tier(self.roles, 'BENEFICIARY')

    @has_beneficiary_role.expression
    def has_beneficiary_role(cls):
        return cls._held_roles.has_key('BENEFICIARY')

    @hybrid_property
    def has_token_agent_role(self):
        return AccessControl.has_any_tier(self.roles, 'TOKEN_AGENT')

    @has_token_agent_role.expression
    def has_token_agent_role(cls):
        return cls._held_roles.has_key('TOKEN_AGENT')

    @hybrid_property
    def has_group_account_role(self):
        return AccessControl.has_any_tier(self.roles, 'GROUP_ACCOUNT')

    @has_group_account_role.expression
    def has_group_account_role(cls):
        return cls._held_roles.has_key('GROUP_ACCOUNT')

    @hybrid_property
    def admin_tier(self):
        return self._held_roles.get('ADMIN', None)

    @hybrid_property
    def vendor_tier(self):
        return self._held_roles.get('VENDOR', None)

    # todo: Refactor into above roles
    # These two are here to interface with the mobile API
    @hybrid_property
    def is_vendor(self):
        return AccessControl.has_sufficient_tier(self.roles, 'VENDOR',
                                                 'vendor')

    @hybrid_property
    def is_supervendor(self):
        return AccessControl.has_sufficient_tier(self.roles, 'VENDOR',
                                                 'supervendor')

    @hybrid_property
    def organisation_ids(self):
        return [organisation.id for organisation in self.organisations]

    @property
    def transfer_account(self):
        active_organisation = getattr(
            g, "active_organisation",
            None) or self.fallback_active_organisation()

        # TODO: Review if this could have a better concept of a default?
        return self.get_transfer_account_for_organisation(active_organisation)

    def get_transfer_account_for_organisation(self, organisation):
        for ta in self.transfer_accounts:
            if ta in organisation.transfer_accounts:
                return ta

        raise Exception(
            f"No matching transfer account for user {self}, token {organisation.token} and organsation {organisation}"
        )

    def get_transfer_account_for_token(self, token):
        return find_transfer_accounts_with_matching_token(self, token)

    def fallback_active_organisation(self):
        if len(self.organisations) == 0:
            return None

        if len(self.organisations) > 1:
            return self.default_organisation

        return self.organisations[0]

    def update_last_seen_ts(self):
        cur_time = datetime.datetime.utcnow()
        if self._last_seen:
            # default to 1 minute intervals
            if cur_time - self._last_seen >= datetime.timedelta(minutes=1):
                self._last_seen = cur_time
        else:
            self._last_seen = cur_time

    @staticmethod
    def salt_hash_secret(password):
        f = Fernet(config.PASSWORD_PEPPER)
        return f.encrypt(bcrypt.hashpw(password.encode(),
                                       bcrypt.gensalt())).decode()

    @staticmethod
    def check_salt_hashed_secret(password, hashed_password):
        f = Fernet(config.PASSWORD_PEPPER)
        hashed_password = f.decrypt(hashed_password.encode())
        return bcrypt.checkpw(password.encode(), hashed_password)

    def hash_password(self, password):
        self.password_hash = self.salt_hash_secret(password)

    def verify_password(self, password):
        return self.check_salt_hashed_secret(password, self.password_hash)

    def hash_pin(self, pin):
        self.pin_hash = self.salt_hash_secret(pin)

    def verify_pin(self, pin):
        return self.check_salt_hashed_secret(pin, self.pin_hash)

    def encode_TFA_token(self, valid_days=1):
        """
        Generates the Auth Token for TFA
        :return: string
        """
        try:

            payload = {
                'exp':
                datetime.datetime.utcnow() +
                datetime.timedelta(days=valid_days, seconds=30),
                'iat':
                datetime.datetime.utcnow(),
                'id':
                self.id
            }

            return jwt.encode(payload,
                              current_app.config['SECRET_KEY'],
                              algorithm='HS256')
        except Exception as e:
            return e

    def encode_auth_token(self):
        """
        Generates the Auth Token
        :return: string
        """
        try:

            payload = {
                'exp':
                datetime.datetime.utcnow() + datetime.timedelta(
                    seconds=current_app.config['AUTH_TOKEN_EXPIRATION']),
                'iat':
                datetime.datetime.utcnow(),
                'id':
                self.id,
                'roles':
                self.roles
            }

            return jwt.encode(payload,
                              current_app.config['SECRET_KEY'],
                              algorithm='HS256')
        except Exception as e:
            return e

    @staticmethod
    def decode_auth_token(auth_token, token_type='Auth'):
        """
        Validates the auth token
        :param auth_token:
        :return: integer|string
        """
        try:
            payload = jwt.decode(auth_token,
                                 current_app.config['SECRET_KEY'],
                                 algorithms='HS256',
                                 options={
                                     'verify_exp':
                                     current_app.config['VERIFY_JWT_EXPIRY']
                                 })

            is_blacklisted_token = BlacklistToken.check_blacklist(auth_token)
            if is_blacklisted_token:
                return 'Token blacklisted. Please log in again.'
            else:
                return payload

        except jwt.ExpiredSignatureError:
            return '{} Token Signature expired.'.format(token_type)
        except jwt.InvalidTokenError:
            return 'Invalid {} Token.'.format(token_type)

    def encode_single_use_JWS(self, token_type):

        s = TimedJSONWebSignatureSerializer(
            current_app.config['SECRET_KEY'],
            expires_in=current_app.config['SINGLE_USE_TOKEN_EXPIRATION'])

        return s.dumps({'id': self.id, 'type': token_type}).decode("utf-8")

    @classmethod
    def decode_single_use_JWS(cls, token, required_type):

        try:
            s = TimedJSONWebSignatureSerializer(
                current_app.config['SECRET_KEY'])

            data = s.loads(token.encode("utf-8"))

            user_id = data.get('id')

            token_type = data.get('type')

            if token_type != required_type:
                return {
                    'success': False,
                    'message': 'Wrong token type (needed %s)' % required_type
                }

            if not user_id:
                return {'success': False, 'message': 'No User ID provided'}

            user = cls.query.filter_by(id=user_id).execution_options(
                show_all=True).first()

            if not user:
                return {'success': False, 'message': 'User not found'}

            return {'success': True, 'user': user}

        except BadSignature:

            return {'success': False, 'message': 'Token signature not valid'}

        except SignatureExpired:

            return {'success': False, 'message': 'Token has expired'}

        except Exception as e:

            return {'success': False, 'message': e}

    def save_password_reset_token(self, password_reset_token):
        # make a "clone" of the existing token list
        self.clear_expired_password_reset_tokens()
        current_password_reset_tokens = self.password_reset_tokens[:]
        current_password_reset_tokens.append(password_reset_token)
        # set db value
        self.password_reset_tokens = current_password_reset_tokens

    def save_pin_reset_token(self, pin_reset_token):
        self.clear_expired_pin_reset_tokens()

        current_pin_reset_tokens = self.pin_reset_tokens[:]
        current_pin_reset_tokens.append(pin_reset_token)

        self.pin_reset_tokens = current_pin_reset_tokens

    def check_reset_token_already_used(self, password_reset_token):
        self.clear_expired_password_reset_tokens()
        is_valid = password_reset_token in self.password_reset_tokens
        return is_valid

    def delete_password_reset_tokens(self):
        self.password_reset_tokens = []

    def delete_pin_reset_tokens(self):
        self.pin_reset_tokens = []

    def clear_expired_reset_tokens(self, token_list):
        if token_list is None:
            token_list = []

        valid_tokens = []
        for token in token_list:
            validity_check = self.decode_single_use_JWS(token, 'R')
            if validity_check['success']:
                valid_tokens.append(token)
        return valid_tokens

    def clear_expired_password_reset_tokens(self):
        tokens = self.clear_expired_reset_tokens(self.password_reset_tokens)
        self.password_reset_tokens = tokens

    def clear_expired_pin_reset_tokens(self):
        tokens = self.clear_expired_reset_tokens(self.pin_reset_tokens)
        self.pin_reset_tokens = tokens

    def create_admin_auth(self,
                          email,
                          password,
                          tier='view',
                          organisation=None):
        self.email = email
        self.hash_password(password)
        self.set_held_role('ADMIN', tier)

        if organisation:
            self.add_user_to_organisation(organisation, is_admin=True)

    def add_user_to_organisation(self,
                                 organisation: Organisation,
                                 is_admin=False):
        if not self.default_organisation:
            self.default_organisation = organisation

        self.organisations.append(organisation)

        if is_admin and organisation.org_level_transfer_account_id:
            if organisation.org_level_transfer_account is None:
                organisation.org_level_transfer_account = (db.session.query(
                    server.models.transfer_account.TransferAccount
                ).execution_options(show_all=True).get(
                    organisation.org_level_transfer_account_id))

            self.transfer_accounts.append(
                organisation.org_level_transfer_account)

    def is_TFA_required(self):
        for tier in current_app.config['TFA_REQUIRED_ROLES']:
            if AccessControl.has_exact_role(self.roles, 'ADMIN', tier):
                return True
        else:
            return False

    def is_TFA_secret_set(self):
        return bool(self._TFA_secret)

    def set_TFA_secret(self):
        secret = pyotp.random_base32()
        self._TFA_secret = encrypt_string(secret)

    def get_TFA_secret(self):
        return decrypt_string(self._TFA_secret)

    def validate_OTP(self, input_otp):
        secret = self.get_TFA_secret()
        server_otp = pyotp.TOTP(secret)
        ret = server_otp.verify(input_otp, valid_window=2)
        return ret

    def set_one_time_code(self, supplied_one_time_code):
        if supplied_one_time_code is None:
            self.one_time_code = str(random.randint(0, 9999)).zfill(4)
        else:
            self.one_time_code = supplied_one_time_code

    # pin as used in mobile. is set to password. we should probably change this to be same as ussd pin
    def set_pin(self, supplied_pin=None, is_activated=False):
        self.is_activated = is_activated

        if not is_activated:
            # Use a one time code, either generated or supplied. PIN will be set to random number for now
            self.set_one_time_code(supplied_one_time_code=supplied_pin)

            pin = str(random.randint(0, 9999)).zfill(4)
        else:
            pin = supplied_pin

        self.hash_password(pin)

    def has_valid_pin(self):
        # not in the process of resetting pin and has a pin
        self.clear_expired_pin_reset_tokens()
        not_resetting = len(self.pin_reset_tokens) == 0

        return self.pin_hash is not None and not_resetting and self.failed_pin_attempts < 3

    def user_details(self):
        # should drop the country code from phone number?
        return "{} {} {}".format(self.first_name, self.last_name, self.phone)

    def get_most_relevant_transfer_usages(self):
        '''Finds the transfer usage/business categories there are most relevant for the user
        based on the last number of send and completed credit transfers supplemented with the
        defaul business categories
        :return: list of most relevant transfer usage objects for the usage
        """
        '''

        sql = text('''
            SELECT *, COUNT(*) FROM
                (SELECT c.transfer_use::text FROM credit_transfer c
                WHERE c.sender_user_id = {} AND c.transfer_status = 'COMPLETE'
                ORDER BY c.updated DESC
                LIMIT 20)
            C GROUP BY transfer_use ORDER BY count DESC
        '''.format(self.id))
        result = db.session.execute(sql)
        most_common_uses = {}
        for row in result:
            if row[0] is not None:
                for use in json.loads(row[0]):
                    most_common_uses[use] = row[1]

        return most_common_uses

    def get_reserve_token(self):
        # reserve token is master token for now
        return Organisation.master_organisation().token

    def __init__(self, blockchain_address=None, **kwargs):
        super(User, self).__init__(**kwargs)

        self.secret = ''.join(
            random.choices(string.ascii_letters + string.digits, k=16))

        self.primary_blockchain_address = blockchain_address or bt.create_blockchain_wallet(
        )

    def __repr__(self):
        if self.has_admin_role:
            return '<Admin {} {}>'.format(self.id, self.email)
        elif self.has_vendor_role:
            return '<Vendor {} {}>'.format(self.id, self.phone)
        else:
            return '<User {} {}>'.format(self.id, self.phone)
Beispiel #27
0
class Store(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    item_group = db.Column(db.Integer)
    price = db.Column(db.Integer)
    limit = db.Column(db.Integer)
Beispiel #28
0
class User(db.Model):
    __tablename__ = 'USER'
    __table_args__ = {'mysql_collate': 'utf8_general_ci'}

    user_idx = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(256), index=True, unique=True)
    user_id = db.Column(db.String(50), index=True, unique=True)
    password = db.Column(db.String(60))
    user_nm = db.Column(db.String(50))
    identity_num = db.Column(db.Integer, index=True, unique=True)
    admin_type = db.Column(db.String(10))
    user_type = db.Column(db.String(1))
    sex_gb = db.Column(db.String(1))
    user_status = db.Column(db.String(1))
    policy_yn = db.Column(db.String(1))
    college_cd = db.Column(db.String(10))
    dpt_cd = db.Column(db.String(10))
    auth_email_yn = db.Column(db.String(1))
    auth_token = db.Column(db.String(20))
    user_profile = db.Column(db.String(100))
    nick_nm = db.Column(db.String(50))
    bamboo_stack = db.Column(db.Integer)
    links = db.Column(db.String(50))
    reg_ip = db.Column(db.String(40))
    reg_dt = db.Column(db.DateTime)
    upt_ip = db.Column(db.String(40))
    upt_dt = db.Column(db.DateTime)
    log_ip = db.Column(db.String(40))
    log_dt = db.Column(db.DateTime)

    def __init__(self,
                 email,
                 user_id,
                 password,
                 user_nm,
                 identity_num,
                 user_type,
                 sex_gb,
                 college_cd,
                 dpt_cd,
                 auth_token,
                 nick_nm,
                 reg_ip,
                 reg_dt,
                 upt_ip,
                 upt_dt,
                 log_ip,
                 log_dt,
                 admin_type='ORD',
                 policy_yn='Y',
                 user_status='Y',
                 auth_email_yn='N',
                 bamboo_stack=None,
                 user_profile='',
                 links=''):
        self.email = email
        self.user_id = user_id
        self.password = password
        self.user_nm = user_nm
        self.identity_num = identity_num
        self.user_type = user_type
        self.sex_gb = sex_gb
        self.user_status = user_status
        self.college_cd = college_cd
        self.dpt_cd = dpt_cd
        self.auth_token = auth_token
        self.nick_nm = nick_nm
        self.reg_dt = reg_dt
        self.reg_ip = reg_ip
        self.upt_dt = upt_dt
        self.upt_ip = upt_ip
        self.log_dt = log_dt
        self.log_ip = log_ip
        self.admin_type = admin_type
        self.policy_yn = policy_yn
        self.auth_email_yn = auth_email_yn
        self.bamboo_stack = bamboo_stack
        self.user_profile = user_profile
        self.links = links

    def __repr__(self):
        return '<User %r>' % self.user_nm

    def as_dict(self):
        return {x.name: getattr(self, x.name) for x in self.__table__.columns}
Beispiel #29
0
class TableName(db.Model):
    __table_args__ = (db.UniqueConstraint('uid', 'name'), )
    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    uid = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    name = db.Column(db.String(80), nullable=False)
    tables = db.relationship('Table', backref='name', lazy=True)
Beispiel #30
0
class Regle(db.Model):
    __tablename__ = 'Regle'
    __bind_key__ = 'plu_tr'

    id_regle = db.Column('id_regle', db.Integer, primary_key=True)
    regle_brute = db.Column('regle_brute', db.String(200))