Exemple #1
0
def _get_identity_service(identity_url=None):
    """Function to return the identity service for the system"""
    if identity_url is None:
        identity_url = _get_identity_url()

    from Acquire.Service import is_running_service as _is_running_service
    if _is_running_service():
        from Acquire.Service import get_trusted_service \
            as _get_trusted_service
        return _get_trusted_service(service_url=identity_url,
                                    service_type='identity')

    from Acquire.Client import LoginError

    try:
        from Acquire.Client import Wallet as _Wallet
        wallet = _Wallet()
        service = wallet.get_service(service_url=identity_url,
                                     service_type="identity")
    except Exception as e:
        from Acquire.Service import exception_to_string
        raise LoginError("Have not received the identity service info from "
                         "the identity service at '%s'\n\nCAUSE: %s" %
                         (identity_url, exception_to_string(e)))

    if not service.can_identify_users():
        raise LoginError(
            "You can only use a valid identity service to log in! "
            "The service at '%s' is a '%s'" %
            (identity_url, service.service_type()))

    return service
    def submit(worksheet_uid, request, par, secret, cheque):
        """Submit a job which has;
           worksheet_uid -

        """
        from Acquire.Service import get_this_service as _get_this_service
        from Acquire.Access import RunRequest as _RunRequest
        from Acquire.Client import PAR as _PAR
        from Acquire.Client import Cheque as _Cheque
        from Acquire.ObjectStore import create_uid as _create_uid
        from Acquire.Compute import Cluster as _Cluster

        if not isinstance(request, _RunRequest):
            raise TypeError("The request must be type RunRequest")

        if not isinstance(par, _PAR):
            raise TypeError("The PAR must be type PAR")

        if not isinstance(cheque, _Cheque):
            raise TypeError("The cheque must be type Cheque")

        service = _get_this_service(need_private_access=True)
        cluster = _Cluster.get_cluster()

        job = ComputeJob()

        job._secret = cluster.encrypt_data(service.decrypt_data(secret))
        job._par = par
        job._request = request

        cost = 10  # TODO - calculate cost of job again from request

        try:
            credit_notes = cheque.cash(spend=cost,
                                       resource="work %s" % worksheet_uid)
        except Exception as e:
            from Acquire.Service import exception_to_string
            from Acquire.Accounting import PaymentError
            raise PaymentError(
                "Problem cashing the cheque used to pay for the calculation: "
                "\n\nCAUSE: %s" % exception_to_string(e))

        if credit_notes is None or len(credit_notes) == 0:
            from Acquire.Accounting import PaymentError
            raise PaymentError("Cannot be paid!")

        job._credit_notes = credit_notes

        job._uid = _create_uid(include_date=True,
                               short_uid=True,
                               separator="/")

        job.save()

        # signal server running on cluster to fetch job and actually
        # submit to slurm
        cluster.submit_job(job._uid)

        return job
Exemple #3
0
def create_service_user_account(service, accounting_service_url):
    """Call this function to create the financial service account
       for this service on the accounting service at 'accounting_service_url'

       This does nothing if the account already exists
    """
    assert_running_service()

    accounting_service = service.get_trusted_service(
        service_url=accounting_service_url)
    accounting_service_uid = accounting_service.uid()

    key = "%s/account/%s" % (_service_key, accounting_service_uid)
    bucket = service.bucket()

    from Acquire.ObjectStore import ObjectStore as _ObjectStore

    try:
        account_uid = _ObjectStore.get_string_object(bucket, key)
    except:
        account_uid = None

    if account_uid:
        # we already have an account...
        return

    service_user = service.login_service_user()

    try:
        from Acquire.Client import create_account as _create_account
        from Acquire.Client import deposit as _deposit

        account = _create_account(
            service_user,
            "main",
            "Main account to receive payment for all use on service "
            "%s (%s)" % (service.canonical_url(), service.uid()),
            accounting_service=accounting_service)

        _deposit(user=service_user,
                 value=100.0,
                 account_name="main",
                 accounting_service=accounting_service)

        account_uid = account.uid()

        _ObjectStore.set_string_object(bucket, key, account_uid)
    except Exception as e:
        from Acquire.Service import exception_to_string
        from Acquire.Service import ServiceAccountError
        raise ServiceAccountError(
            "Unable to create a financial account for the service "
            "principal for '%s' on accounting service '%s'\n\nERROR\n%s" %
            (str(service), str(accounting_service), exception_to_string(e)))
Exemple #4
0
def run(args):
    """This function is called to handle request to cash cheques. This
       will verify that the cheque is valid and will then create
       the debit/credit note pair for the transation. It will return
       the CreditNote to the caller so they can see that the funds have
       been reserved, and can receipt the transaction once goods/services
       have been delivered.

       Args:
            args (dict): information for payment for service

        Returns:
            dict: contains status, status message and credit note if valid

    """

    credit_notes = []

    try:
        cheque = args["cheque"]
    except:
        raise ValueError("You must supply a cheque to be cashed!")

    try:
        cheque = Cheque.from_data(cheque)
    except Exception as e:
        from Acquire.Service import exception_to_string
        raise TypeError("Unable to interpret the cheque.\n\nCAUSE: %s" %
                        exception_to_string(e))

    try:
        spend = args["spend"]
    except:
        spend = None

    if spend is not None:
        try:
            spend = string_to_decimal(spend)
        except Exception as e:
            from Acquire.Service import exception_to_string
            raise TypeError("Unable to interpret the spend.\n\nCause: %s" %
                            exception_to_string(e))

    try:
        resource = str(args["resource"])
    except:
        raise ValueError(
            "You must supply a string representing the resource that will "
            "be paid for using this cheque")

    try:
        account_uid = str(args["account_uid"])
    except:
        raise ValueError("You must supply the UID of the account to which the "
                         "cheque will be cashed")

    try:
        receipt_by = args["receipt_by"]
    except:
        raise ValueError(
            "You must supply the datetime by which you promise to "
            "receipt this transaction")

    try:
        receipt_by = string_to_datetime(receipt_by)
    except Exception as e:
        from Acquire.Service import exception_to_string
        raise TypeError(
            "Unable to interpret the receipt_by date.\n\nCAUSE: %s" %
            exception_to_string(e))

    # now read the cheque - this will only succeed if the cheque
    # is valid, has been signed, has been sent from the right
    # service, and was authorised by the user, the cheque
    # has not expired and we are the
    # service which holds the account from which funds are drawn
    info = cheque.read(resource=resource, spend=spend, receipt_by=receipt_by)

    try:
        description = str(args["description"])
    except:
        description = info["resource"]

    authorisation = info["authorisation"]
    auth_resource = info["auth_resource"]
    user_guid = authorisation.user_guid()

    # the cheque is valid
    bucket = get_service_account_bucket()

    try:
        debit_account = Account(uid=info["account_uid"], bucket=bucket)
    except Exception as e:
        from Acquire.Service import exception_to_string
        raise PaymentError("Cannot find the account associated with the cheque"
                           "\n\nCAUSE: %s" % exception_to_string(e))

    try:
        credit_account = Account(uid=account_uid, bucket=bucket)
    except Exception as e:
        from Acquire.Service import exception_to_string
        raise PaymentError(
            "Cannot find the account to which funds will be creditted:"
            "\n\nCAUSE: %s" % exception_to_string(e))

    # validate that this account is in a group that can be authorised
    # by the user (this should eventually go as the ACLs now allow users
    # to authorised payments from many accounts)
    accounts = Accounts(user_guid=user_guid)
    if not accounts.contains(account=debit_account, bucket=bucket):
        raise PermissionError(
            "The user with UID '%s' cannot authorise transactions from "
            "the account '%s' as they do not own this account." %
            (user_guid, str(debit_account)))

    transaction = Transaction(value=info["spend"], description=description)

    # we have enough information to perform the transaction
    # - this is provisional as the service must receipt everything
    transaction_records = Ledger.perform(transactions=transaction,
                                         debit_account=debit_account,
                                         credit_account=credit_account,
                                         authorisation=authorisation,
                                         authorisation_resource=auth_resource,
                                         is_provisional=True,
                                         receipt_by=receipt_by,
                                         bucket=bucket)

    # extract all of the credit notes to return to the user,
    # and also to record so that we can check if they have not
    # been receipted in time...
    credit_notes = []

    for record in transaction_records:
        credit_notes.append(record.credit_note())

    credit_notes = list_to_string(credit_notes)

    receipt_key = "accounting/cashed_cheque/%s" % info["uid"]
    mutex = Mutex(receipt_key, bucket=bucket)

    try:
        receipted = ObjectStore.get_object_from_json(bucket, receipt_key)
    except:
        receipted = None

    if receipted is not None:
        # we have tried to cash this cheque twice!
        mutex.unlock()
        Ledger.refund(transaction_records, bucket=bucket)
    else:
        info = {"status": "needs_receipt", "creditnotes": credit_notes}
        ObjectStore.set_object_from_json(bucket, receipt_key, info)
        mutex.unlock()

    return {"credit_notes": credit_notes}
def main():
    import argparse
    import sys

    from Acquire.Client import Wallet, LoginError

    parser = argparse.ArgumentParser(
        description="Log into an Acquire-based identity "
        "service via a login url",
        prog="acquire_login")

    parser.add_argument("url", type=str, nargs="*", help="Login URL")

    parser.add_argument('-u',
                        '--username',
                        type=str,
                        nargs='?',
                        help="Username with which to log in")

    parser.add_argument('--remember-password',
                        action="store_true",
                        default=True,
                        help="Remember the password (default on)")

    parser.add_argument('--remember-device',
                        action="store_true",
                        default=None,
                        help="Remember this device (saves OTP code, "
                        "default off)")

    parser.add_argument('--no-remember-device',
                        action="store_true",
                        default=None,
                        help="Don't remember this device, and don't ask to")

    parser.add_argument('--no-remember-password',
                        action="store_true",
                        default=None,
                        help="Don't remember the password, and don't ask to")

    parser.add_argument('--remove-service',
                        type=str,
                        nargs="*",
                        help="Remove locally stored information about the "
                        "passed service(s)")

    parser.add_argument('--dry-run',
                        action="store_true",
                        default=None,
                        help="Do a dry-run of the login - don't connect to "
                        "the server")

    args = parser.parse_args()

    remember_device = args.remember_device

    if args.no_remember_device:
        remember_device = False

    remember_password = args.remember_password

    if remember_password is None:
        remember_password = True

    if args.no_remember_password:
        remember_password = False

    dryrun = args.dry_run

    if not remember_password:
        # should not remember the otpsecret if
        # we don't trust this to remember the password!
        remember_device = False

    do_nothing = True

    wallet = Wallet()

    if args.remove_service:
        for service in args.remove_service:
            try:
                do_nothing = False
                print("Removing locally stored information "
                      "about service '%s'" % service)
                wallet.remove_service(service)
            except Exception as e:
                print(e)
                pass

    if do_nothing and len(args.url) == 0:
        parser.print_help(sys.stdout)

    if len(args.url) == 0:
        sys.exit(0)

    for url in args.url:
        try:
            wallet.send_password(url=url,
                                 username=args.username,
                                 remember_password=remember_password,
                                 remember_device=remember_device,
                                 dryrun=dryrun)
        except LoginError as e:
            print("\n%s" % e.args)
        except Exception as e:
            from Acquire.Service import exception_to_string
            print(exception_to_string(e))
Exemple #6
0
    def execute(self, cheque):
        """Execute (start) this work, using the passed cheque for
           payment. Note that you can't perform the same work twice
        """
        if self.is_null():
            from Acquire.Accounting import PaymentError
            raise PaymentError("You cannot try to execute null work!")

        from Acquire.Client import Cheque as _Cheque
        if not isinstance(cheque, _Cheque):
            raise TypeError("You must pass a valid Cheque as payment "
                            "for the work")

        if self._credit_notes is not None:
            raise PermissionError("You cannot start a piece of work twice!")

        from Acquire.Service import get_this_service as _get_this_service

        access_service = _get_this_service(need_private_access=True)
        compute_service = self.compute_service()
        storage_service = self.storage_service()
        accounting_service = cheque.accounting_service()

        access_user = access_service.login_service_user()

        account_uid = access_service.service_user_account_uid(
            accounting_service=accounting_service)

        from Acquire.Client import Account as _Account
        access_account = _Account(user=access_user,
                                  account_uid=account_uid,
                                  accounting_service=accounting_service)

        # TODO - validate that the cost of the work on the compute
        #        and storage services is covered by the passed cheque

        try:
            credit_notes = cheque.cash(spend=self.total_cost(),
                                       resource=self.request().fingerprint())
        except Exception as e:
            from Acquire.Service import exception_to_string
            from Acquire.Accounting import PaymentError
            raise PaymentError(
                "Problem cashing the cheque used to pay for the calculation: "
                "\n\nCAUSE: %s" % exception_to_string(e))

        if credit_notes is None or len(credit_notes) == 0:
            from Acquire.Accounting import PaymentError
            raise PaymentError("Cannot be paid!")

        # make sure that we have been paid!
        for credit_note in credit_notes:
            if credit_note.credit_account_uid() != access_account.uid():
                raise PaymentError("The wrong account has been paid!?!")

        self._status = "awaiting (paid)"
        self._credit_notes = credit_notes

        # work out when this job MUST have finished. If the job
        # has not completed before this time then it will be killed
        from Acquire.ObjectStore import get_datetime_future \
            as _get_datetime_future

        endtime = _get_datetime_future(days=2)  # this should be calculated

        # save the WorkSheet to the object store so we don't lose the
        # value in the credit notes
        self.save()

        compute_cheque = _Cheque.write(
            account=access_account,
            resource="work %s" % self.uid(),
            max_spend=10.0,
            recipient_url=compute_service.canonical_url(),
            expiry_date=endtime)

        storage_cheque = _Cheque.write(
            account=access_account,
            resource="work %s" % self.uid(),
            max_spend=10.0,
            recipient_url=storage_service.canonical_url(),
            expiry_date=endtime)

        self._compute_cheque = compute_cheque
        self._storage_cheque = storage_cheque

        # now create a Drive on the storage service that will hold
        # the output for this job
        from Acquire.Client import Drive as _Drive
        from Acquire.Client import StorageCreds as _StorageCreds
        from Acquire.Client import ACLRule as _ACLRule
        from Acquire.Client import ACLRules as _ACLRules
        from Acquire.Client import ACLUserRules as _ACLUserRules

        creds = _StorageCreds(user=access_user,
                              storage_service=storage_service)

        rule = _ACLUserRules.owner(user_guid=access_user.guid()).add(
            user_guid=self.user_guid(), rule=_ACLRule.reader())

        aclrules = _ACLRules(rule=rule, default_rule=_ACLRule.denied())

        output_drive = _Drive(name="output_%s" % self.uid(),
                              creds=creds,
                              aclrules=aclrules,
                              cheque=storage_cheque,
                              max_size="10MB",
                              autocreate=True)

        self._output_loc = output_drive.metadata().location()
        self._status = "awaiting (paid, have drive)"
        self.save()

        from Acquire.Client import PAR as _PAR
        par = _PAR(location=self._output_loc,
                   user=access_user,
                   aclrule=_ACLRule.writer(),
                   expires_datetime=endtime)

        secret = compute_service.encrypt_data(par.secret())

        args = {
            "worksheet_uid": self.uid(),
            "request": self.request().to_data(),
            "par": par.to_data(),
            "secret": secret,
            "cheque": compute_cheque.to_data()
        }

        self._status = "submitting"
        self.save()

        response = compute_service.call_function(function="submit_job",
                                                 args=args)

        print(response)

        self._status = "submitted"
        self.save()

        # the service user will log out automatically on destruction, but
        # let us make sure!
        access_user.logout()