diff --git a/install.py b/install.py index a0e5126..b00e98b 100644 --- a/install.py +++ b/install.py @@ -1,5 +1,7 @@ import os os.system("pip install -r requirements.txt") -os.system("cp local_settings_default.py multiexplorer/multiexplorer/local_settings.py") +os.system( + "cp local_settings_default.py multiexplorer/multiexplorer/local_settings.py") os.system("python multiexplorer/manage.py migrate") +os.system("python multiexplorer/manage.py createcachetable") diff --git a/multiexplorer/exchange/admin.py b/multiexplorer/exchange/admin.py index ed6d277..3e093f6 100644 --- a/multiexplorer/exchange/admin.py +++ b/multiexplorer/exchange/admin.py @@ -2,6 +2,7 @@ from django.contrib import admin from .models import ExchangeCurrency, ExchangeMasterKey, ExchangeAddress + class ExchangeMasterKeyAdmin(admin.ModelAdmin): list_display = ("created", 'notes', 'balances') readonly_fields = ('xpriv', ) @@ -16,6 +17,7 @@ def balances(self, master_key): return balances balances.allow_tags = True + class ExchangeCurrencyAdmin(admin.ModelAdmin): list_display = ("get_currency_display", 'balance', 'deposit') readonly_fields = ('balance', 'deposit_qrcodes') @@ -45,6 +47,7 @@ def deposit_qrcodes(self, currency): ) deposit_qrcodes.allow_tags = True + class ExchangeAddressAdmin(admin.ModelAdmin): list_display = ('deposit_currency', 'withdraw_address') diff --git a/multiexplorer/exchange/models.py b/multiexplorer/exchange/models.py index ab15b62..2f312ad 100644 --- a/multiexplorer/exchange/models.py +++ b/multiexplorer/exchange/models.py @@ -11,7 +11,9 @@ from multiexplorer.utils import get_wallet_currencies from multiexplorer.models import CachedTransaction -SUPPORTED_CURRENCIES = [(x['bip44'], "%(code)s - %(name)s" % x) for x in get_wallet_currencies()] +SUPPORTED_CURRENCIES = [(x['bip44'], "%(code)s - %(name)s" % x) + for x in get_wallet_currencies()] + class ExchangeMasterKey(models.Model): created = models.DateTimeField(default=timezone.now) @@ -25,7 +27,8 @@ class Meta: def save(self, *args, **kwargs): if not self.xpriv: - seed_words = self.from_passphrase and self.from_passphrase.encode('ascii') + seed_words = self.from_passphrase and self.from_passphrase.encode( + 'ascii') self.xpriv = bip32_master_key(seed=seed_words or os.urandom(100)) self.from_passphrase = '' @@ -44,15 +47,17 @@ def cached_balance(self, currency): for utxo in utxos: balance += utxo['amount'] - return balance / 1e8 # convert satoshis to currency units + return balance / 1e8 # convert satoshis to currency units def get_unused_addresses(self, currency, length=5): i = 0 addresses = [] while len(addresses) < length: address, priv_key = currency.derive_deposit_address(i, self) - is_used1 = ManualDeposit.objects.filter(address=address, currency=currency).exists() - is_used2 = ExchangeAddress.objects.filter(deposit_address=address, deposit_currency__code=currency).exists() + is_used1 = ManualDeposit.objects.filter( + address=address, currency=currency).exists() + is_used2 = ExchangeAddress.objects.filter( + deposit_address=address, deposit_currency__code=currency).exists() if not is_used1 and not is_used2: addresses.append(address) @@ -62,18 +67,22 @@ def get_unused_addresses(self, currency, length=5): FIAT_CHOICES = [(x, x.upper()) for x in settings.WALLET_SUPPORTED_FIATS] + class ExchangeCurrency(models.Model): - currency = models.IntegerField(choices=SUPPORTED_CURRENCIES, primary_key=True) + currency = models.IntegerField( + choices=SUPPORTED_CURRENCIES, primary_key=True) fee_percentage = models.FloatField(default=0.01) code = models.CharField(max_length=5, null=True, blank=True) max_fiat_deposit = models.FloatField(default=10) - max_fiat_currency = models.CharField(max_length=4, choices=FIAT_CHOICES, default='usd') + max_fiat_currency = models.CharField( + max_length=4, choices=FIAT_CHOICES, default='usd') @classmethod def get_active(cls, code): obj = cls.objects.get(code=code) if obj.balance == 0: - raise cls.DoesNotExist("%s is temporairly out of order" % code.upper()) + raise cls.DoesNotExist( + "%s is temporairly out of order" % code.upper()) return obj def __unicode__(self): @@ -113,7 +122,8 @@ def get_bip44_master(self, master_key): return bip32_ckd(bip32_ckd(master_key.xpriv, 44), self.currency) def _derive_address(self, change_or_deposit, index, master_key): - xpriv = bip32_ckd(bip32_ckd(self.get_bip44_master(master_key), change_or_deposit), index) + xpriv = bip32_ckd(bip32_ckd(self.get_bip44_master( + master_key), change_or_deposit), index) priv = bip32_extract_key(xpriv) address = privtoaddr(priv, self.get_address_byte()) return address, priv @@ -174,13 +184,16 @@ class ExchangeAddress(models.Model): last_kick = models.DateTimeField(default=None, null=True) # the user sends funds to this address (address generated by us) - deposit_currency = models.ForeignKey(ExchangeCurrency, related_name="incoming_exchanges") + deposit_currency = models.ForeignKey( + ExchangeCurrency, related_name="incoming_exchanges") deposit_address = models.CharField(max_length=128) exchange_rate = models.FloatField(null=True) - # we send the exchanged funds to this address (address supplied by the user) - withdraw_currency = models.ForeignKey(ExchangeCurrency, related_name="outgoing_exchanges") + # we send the exchanged funds to this address (address supplied by the + # user) + withdraw_currency = models.ForeignKey( + ExchangeCurrency, related_name="outgoing_exchanges") withdraw_address = models.CharField(max_length=128, blank=True) def __unicode__(self): @@ -192,29 +205,35 @@ def seconds_to_needing_kick(self): return (settings.EXCHANGE_KICK_INTERVAL_MINUTES * 60) - since_last_kick def kick(self): - if self.last_kick: # already been kicked + if self.last_kick: # already been kicked if self.seconds_to_needing_kick() < 0: - return # kick not needed + return # kick not needed if not self.withdraw_address: master_key = ExchangeMasterKey.objects.latest() - self.withdraw_address = master_key.get_unused_addresses(self.withdraw_currency, 1)[0] + self.withdraw_address = master_key.get_unused_addresses( + self.withdraw_currency, 1)[0] if not self.deposit_address: master_key = ExchangeMasterKey.objects.latest() - self.deposit_address = master_key.get_unused_addresses(self.deposit_currency, 1)[0] + self.deposit_address = master_key.get_unused_addresses( + self.deposit_currency, 1)[0] self.last_kick = timezone.now() self.exchange_rate = self.calculate_exchange_rate() self.save() def calculate_exchange_rate(self): - deposit_price = get_current_price(crypto=self.deposit_currency.code, fiat='usd') - withdraw_price = get_current_price(crypto=self.withdraw_currency.code, fiat='usd') + deposit_price = get_current_price( + crypto=self.deposit_currency.code, fiat='usd') + withdraw_price = get_current_price( + crypto=self.withdraw_currency.code, fiat='usd') raw_rate = deposit_price / withdraw_price - rate_with_fee = raw_rate * (1 + (settings.EXCHANGE_FEE_PERCENTAGE / 100.0)) + rate_with_fee = raw_rate * \ + (1 + (settings.EXCHANGE_FEE_PERCENTAGE / 100.0)) return rate_with_fee + class ExchangeWithdraw(models.Model): exchange = models.ForeignKey(ExchangeAddress) deposit_txid = models.TextField() @@ -230,6 +249,7 @@ def withdraw_amount(self): def make_payment(self): tx = Transaction(crypto=self.withdraw_currency.code) - tx.add_inputs(self.exchange.withdraw_currency.pop_utxos(self.withdraw_amount)) + tx.add_inputs(self.exchange.withdraw_currency.pop_utxos( + self.withdraw_amount)) tx.add_output(self.withdraw_amount, to_address) return tx.push_tx() diff --git a/multiexplorer/exchange/urls.py b/multiexplorer/exchange/urls.py index d6daac7..fac1b71 100644 --- a/multiexplorer/exchange/urls.py +++ b/multiexplorer/exchange/urls.py @@ -2,9 +2,14 @@ from django.contrib import admin from django.views.generic import TemplateView -from views import ( - home, create_exchange -) +try: + from views import ( + home, create_exchange + ) +except ImportError: + from .views import ( + home, create_exchange + ) urlpatterns = [ url(r'^$', home, name="exchange"), diff --git a/multiexplorer/exchange/views.py b/multiexplorer/exchange/views.py index b9eec79..2f259ea 100644 --- a/multiexplorer/exchange/views.py +++ b/multiexplorer/exchange/views.py @@ -4,14 +4,19 @@ from django import http from django.conf import settings from django.views.decorators.csrf import csrf_exempt +from django.shortcuts import redirect -from models import ExchangeCurrency, ExchangeAddress +try: + from models import ExchangeCurrency, ExchangeAddress +except ImportError: + from .models import ExchangeCurrency, ExchangeAddress from multiexplorer.utils import get_wallet_currencies crypto_data = get_wallet_currencies() crypto_data_json = json.dumps(crypto_data) + def home(request): return TemplateResponse(request, "exchange_home.html", { 'supported_cryptos': ExchangeCurrency.objects.all(), @@ -19,39 +24,43 @@ def home(request): 'ENABLE_EXCHANGE': settings.ENABLE_EXCHANGE }) + @csrf_exempt def create_exchange(request): - deposit_code = request.POST['deposit_code'] - withdraw_address = request.POST['withdraw_address'] - withdraw_code = request.POST['withdraw_code'] - error = None - - try: - withdraw_currency = ExchangeCurrency.get_active(code=withdraw_code) - except ExchangeCurrency.DoesNotExist as exc: - error = 'withdraw' - error_msg = exc.__str__ - - try: - deposit_currency = ExchangeCurrency.get_active(code=deposit_code) - except ExchangeCurrency.DoesNotExist as exc: - error = 'deposit' - error_msg = exc.__str__ - - if error: + if request.method == "POST": + deposit_code = request.POST['deposit_code'] + withdraw_address = request.POST['withdraw_address'] + withdraw_code = request.POST['withdraw_code'] + error = None + + try: + withdraw_currency = ExchangeCurrency.get_active(code=withdraw_code) + except ExchangeCurrency.DoesNotExist as exc: + error = 'withdraw' + error_msg = exc.__str__ + + try: + deposit_currency = ExchangeCurrency.get_active(code=deposit_code) + except ExchangeCurrency.DoesNotExist as exc: + error = 'deposit' + error_msg = exc.__str__ + + if error: + return http.JsonResponse({ + 'error': error_msg + }, status=400) + + exchange, created = ExchangeAddress.objects.get_or_create( + deposit_currency=deposit_currency, + withdraw_address=withdraw_address, + withdraw_currency=withdraw_currency + ) + exchange.kick() + return http.JsonResponse({ - 'error': error_msg - }, status=400) - - exchange, created = ExchangeAddress.objects.get_or_create( - deposit_currency=deposit_currency, - withdraw_address=withdraw_address, - withdraw_currency=withdraw_currency - ) - exchange.kick() - - return http.JsonResponse({ - 'deposit_address': exchange.deposit_address, - 'exchange_rate': exchange.exchange_rate, - 'seconds_left': exchange.seconds_to_needing_kick(), - }) + 'deposit_address': exchange.deposit_address, + 'exchange_rate': exchange.exchange_rate, + 'seconds_left': exchange.seconds_to_needing_kick(), + }) + else: + return redirect("exchange") diff --git a/multiexplorer/multiexplorer/admin.py b/multiexplorer/multiexplorer/admin.py index e79d2d9..76bd863 100644 --- a/multiexplorer/multiexplorer/admin.py +++ b/multiexplorer/multiexplorer/admin.py @@ -3,6 +3,7 @@ from .models import CachedTransaction from django.utils.safestring import mark_safe + class CachedTransactionAdmin(admin.ModelAdmin): list_display = ("txid", 'crypto', "content_length") readonly_fields = ('pretty_print', ) diff --git a/multiexplorer/multiexplorer/models.py b/multiexplorer/multiexplorer/models.py index f45e276..a58e634 100644 --- a/multiexplorer/multiexplorer/models.py +++ b/multiexplorer/multiexplorer/models.py @@ -3,6 +3,7 @@ from .utils import datetime_to_iso from moneywagon import get_single_transaction, get_block + class CachedTransaction(models.Model): txid = models.CharField(max_length=72, primary_key=True) content = models.TextField() @@ -17,8 +18,10 @@ def fetch_full_tx(cls, crypto, txid, existing_tx_data=None): transaction = json.loads(tx.content) if existing_tx_data and existing_tx_data.get('confirmations', None): - # update cached entry with updated confirmations if its available - transaction['confirmations'] = existing_tx_data['confirmations'] + # update cached entry with updated confirmations if its + # available + transaction['confirmations'] = existing_tx_data[ + 'confirmations'] tx.content = json.dumps(transaction) tx.save() return transaction diff --git a/multiexplorer/multiexplorer/urls.py b/multiexplorer/multiexplorer/urls.py index b325577..236b7cd 100644 --- a/multiexplorer/multiexplorer/urls.py +++ b/multiexplorer/multiexplorer/urls.py @@ -2,10 +2,16 @@ from django.contrib import admin from django.views.generic import TemplateView -from views import ( - home, perform_lookup, single_address, block_lookup, api_docs, address_disambiguation, - onchain_exchange_rates, onchain_status, logout, single_tx -) +try: + from views import ( + home, perform_lookup, single_address, block_lookup, api_docs, address_disambiguation, + onchain_exchange_rates, onchain_status, logout, single_tx + ) +except ImportError: + from .views import ( + home, perform_lookup, single_address, block_lookup, api_docs, address_disambiguation, + onchain_exchange_rates, onchain_status, logout, single_tx + ) admin.site.site_header = 'MultiExplorer Administration' @@ -22,12 +28,15 @@ url(r'^tx/(?P\w{3,5})/(?P\w+)', single_tx, name="single_tx"), url(r'^api$', api_docs, name="api_docs"), - url(r'^api/onchain_exchange_rates', onchain_exchange_rates, name="onchain_exchange_rates"), + url(r'^api/onchain_exchange_rates', + onchain_exchange_rates, name="onchain_exchange_rates"), url(r'^api/onchain_exchange_status', onchain_status, name="onchain_status"), - url(r'^api/(?P\w+)/(?P\w+)', perform_lookup, name="perform_lookup"), + url(r'^api/(?P\w+)/(?P\w+)', + perform_lookup, name="perform_lookup"), - url(r'^disambiguation/(?P
\w+)', address_disambiguation, name="address_disambiguation"), + url(r'^disambiguation/(?P
\w+)', + address_disambiguation, name="address_disambiguation"), url(r'^wallet/', include('wallet.urls')), url(r'^exchange/', include('exchange.urls')), diff --git a/multiexplorer/multiexplorer/utils.py b/multiexplorer/multiexplorer/utils.py index f40712e..290dc75 100644 --- a/multiexplorer/multiexplorer/utils.py +++ b/multiexplorer/multiexplorer/utils.py @@ -28,7 +28,8 @@ def make_crypto_data_json(): del ret[currency] continue - ret[currency]['address_version_byte'] = data.get('address_version_byte', None) + ret[currency]['address_version_byte'] = data.get( + 'address_version_byte', None) for mode in service_modes: services = data.get('services', {}).get(mode, []) diff --git a/multiexplorer/multiexplorer/views.py b/multiexplorer/multiexplorer/views.py index c406201..b3fcab7 100644 --- a/multiexplorer/multiexplorer/views.py +++ b/multiexplorer/multiexplorer/views.py @@ -32,6 +32,7 @@ block_info_currencies = get_block_currencies() service_table_html = service_table(format='html') + @csrf_exempt def perform_lookup(request, service_mode, service_id): """ @@ -102,7 +103,7 @@ def perform_lookup(request, service_mode, service_id): 'error': "Currency Not Recognized: %s" % currency_name }, status=400) - if service_mode == 'push_tx': # don't go inside cache function + if service_mode == 'push_tx': # don't go inside cache function try: result = push_tx(currency, request.POST['tx']) return http.JsonResponse({ @@ -121,18 +122,21 @@ def perform_lookup(request, service_mode, service_id): def _cached_fetch(service_mode, service_id, address=None, addresses=None, xpub=None, - currency=None, currency_name=None, fiat=None, include_raw=False, Service=None, block_args=None, - extended_fetch=False, txid=None, random_mode=False, **k): + currency=None, currency_name=None, fiat=None, include_raw=False, + Service=None, block_args=None, extended_fetch=False, txid=None, + random_mode=False, **k): if not block_args: block_args = {} - key_ending = address or xpub or fiat or (''.join([x[:5] for x in addresses]) or "".join(block_args.values())) + key_ending = address or xpub or fiat or ( + ''.join([x[:5] for x in addresses]) or "".join(block_args.values())) if extended_fetch: key_ending += "--ExtendedFetch" - cache_key = '%s:%s:%s:%s' % (currency.lower(), service_mode, service_id, key_ending) + cache_key = '%s:%s:%s:%s' % ( + currency.lower(), service_mode, service_id, key_ending) hit = cache.get(cache_key) if hit: @@ -141,7 +145,8 @@ def _cached_fetch(service_mode, service_id, address=None, addresses=None, xpub=N try: response_dict = _make_moneywagon_fetch(**locals()) if extended_fetch: - response_dict = _do_extended_fetch(currency, response_dict['transactions']) + response_dict = _do_extended_fetch( + currency, response_dict['transactions']) except Exception as exc: return True, {'error': "%s: %s" % (exc.__class__.__name__, str(exc))} @@ -169,13 +174,15 @@ def _cached_fetch(service_mode, service_id, address=None, addresses=None, xpub=N {'name': x['name'], 'id': x['id']} for x in services ] - response_dict['fetched_seconds_ago'] = int(time.time()) - response_dict['timestamp'] + response_dict['fetched_seconds_ago'] = int( + time.time()) - response_dict['timestamp'] del response_dict['timestamp'] return None, response_dict -def _make_moneywagon_fetch(Service, service_mode, service_id, address, addresses, - xpub, currency, currency_name, block_args, fiat=None, txid=None, random_mode=False, **k): +def _make_moneywagon_fetch(Service, service_mode, service_id, address, + addresses, xpub, currency, currency_name, + block_args, fiat=None, txid=None, random_mode=False, **k): if Service: if Service.supported_cryptos and currency not in Service.supported_cryptos: @@ -184,7 +191,7 @@ def _make_moneywagon_fetch(Service, service_mode, service_id, address, addresses )) services = [Service] else: - services = [] # fallback mode + services = [] # fallback mode modes = dict( report_services=True, @@ -233,7 +240,7 @@ def _make_moneywagon_fetch(Service, service_mode, service_id, address, addresses raise Exception("Unsupported Service mode") if not used_services: - pass # private mode does not return services + pass # private mode does not return services elif len(used_services) == 1: s = used_services[0] if s: @@ -243,12 +250,14 @@ def _make_moneywagon_fetch(Service, service_mode, service_id, address, addresses ret['service_id'] = s.service_id else: ret['services'] = [ - {'name': s.name, 'id': s.service_id, 'raw_response': s.last_raw_response.json()} + {'name': s.name, 'id': s.service_id, + 'raw_response': s.last_raw_response.json()} for s in used_services ] return ret + def _do_extended_fetch(crypto, transactions): """ `transactions` is a list of transactions that comes from get_historical_transactions @@ -340,6 +349,7 @@ def api_docs(request): 'domain': "multiexplorer.com", }) + def address_disambiguation(request, address): """ When the user tried to lookup a currency that has a duplicate address byte @@ -360,6 +370,7 @@ def address_disambiguation(request, address): 'address': address, }) + def onchain_exchange_rates(request): """ Returns a list of exchange pairs that are supported by all onchain exchange services. @@ -405,13 +416,14 @@ def onchain_exchange_rates(request): 'provider': 'ShapeShift.io' }) - return http.JsonResponse({'pairs': final_pairs}) + def logout(request): dj_logout(request) return http.HttpResponseRedirect("/") + def onchain_status(request): deposit_crypto = request.GET['deposit_currency'] address = request.GET['address'] @@ -419,6 +431,7 @@ def onchain_status(request): response = requests.get("https://shapeshift.io/txStat/" + address).json() return http.JsonResponse(response) + def single_tx(request, crypto, txid): full_tx = CachedTransaction.fetch_full_tx(crypto, txid=txid) return TemplateResponse(request, "single_transaction.html", { diff --git a/multiexplorer/wallet/admin.py b/multiexplorer/wallet/admin.py index db6ef3f..cd4a1c5 100644 --- a/multiexplorer/wallet/admin.py +++ b/multiexplorer/wallet/admin.py @@ -2,6 +2,7 @@ from .models import WalletMasterKeys # Register your models here. + class MasterAdmin(admin.ModelAdmin): readonly_fields = ('encrypted_mnemonic', ) diff --git a/multiexplorer/wallet/models.py b/multiexplorer/wallet/models.py index 0be7137..27ef2b4 100644 --- a/multiexplorer/wallet/models.py +++ b/multiexplorer/wallet/models.py @@ -13,6 +13,7 @@ (0, "Never"), ) + class WalletMasterKeys(models.Model): """ Stores the BIP32 HD master seed for the user's wallet. @@ -23,10 +24,14 @@ class WalletMasterKeys(models.Model): auto_logout = models.IntegerField(choices=AUTO_LOGOUT_CHOICES, default=0) display_fiat = models.CharField(max_length=5, default='usd') - show_wallet_list = models.TextField(default='btc,ltc,doge,dash') # comma seperated + show_wallet_list = models.TextField( + default='btc,ltc,doge,dash') # comma seperated def __unicode__(self): - return self.user.username + return "Master key of user: %s (UID: %d)" % (self.user.username, self.user.id) + + def __str__(self): + return self.__unicode__() def get_show_wallet_list(self): return self.show_wallet_list.split(",") @@ -50,6 +55,7 @@ def get_settings(self): 'auto_logout': self.auto_logout } + class FailedLogin(models.Model): username = models.CharField(max_length=64, db_index=True) time = models.DateTimeField(default=datetime.datetime.now) diff --git a/multiexplorer/wallet/templates/register.html b/multiexplorer/wallet/templates/register.html index f9e774a..f6aaa83 100644 --- a/multiexplorer/wallet/templates/register.html +++ b/multiexplorer/wallet/templates/register.html @@ -30,58 +30,88 @@

Register a New Account

diff --git a/multiexplorer/wallet/urls.py b/multiexplorer/wallet/urls.py index 37a2682..f27daa9 100644 --- a/multiexplorer/wallet/urls.py +++ b/multiexplorer/wallet/urls.py @@ -2,11 +2,15 @@ from django.contrib import admin from django.views.generic import TemplateView -from views import home, register_new_wallet_user, login, save_settings +try: + from views import home, register_new_wallet_user, login, save_settings +except ImportError: + from .views import home, register_new_wallet_user, login, save_settings urlpatterns = [ url(r'^$', home, name="wallet"), - url(r'^register_new_wallet_user', register_new_wallet_user, name="register_new_wallet_user"), + url(r'^register_new_wallet_user', register_new_wallet_user, + name="register_new_wallet_user"), url(r'^login', login, name='login'), url(r'^save_settings', save_settings, name="save_wallet_settings"), ] diff --git a/multiexplorer/wallet/views.py b/multiexplorer/wallet/views.py index 72bddad..08d2a2f 100644 --- a/multiexplorer/wallet/views.py +++ b/multiexplorer/wallet/views.py @@ -7,6 +7,8 @@ from django.utils import timezone from django.template.response import TemplateResponse from django import http +from django.shortcuts import redirect +from django.db.utils import IntegrityError from .models import WalletMasterKeys, FailedLogin, AUTO_LOGOUT_CHOICES from multiexplorer.utils import get_wallet_currencies @@ -16,6 +18,7 @@ from multiexplorer.views import _cached_fetch + def get_rates(fiat): rates = {} for data in crypto_data: @@ -33,13 +36,14 @@ def get_rates(fiat): } return rates + def home(request): return TemplateResponse(request, "wallet_home.html", { 'crypto_data_json': crypto_data_json, 'crypto_data': crypto_data, 'supported_fiats': settings.WALLET_SUPPORTED_FIATS, 'autologout_choices': AUTO_LOGOUT_CHOICES, - 'use_inverted_colors': ['vtc', 'uno'], # makes spinner white + 'use_inverted_colors': ['vtc', 'uno'], # makes spinner white }) @@ -64,28 +68,35 @@ def save_settings(request): def register_new_wallet_user(request): - encrypted_mnemonic = request.POST['encrypted_mnemonic'] - password = request.POST['password'] - username = request.POST['username'] - - user = User.objects.create( - username=username, - email=request.POST.get('email', ''), - ) - user.set_password(password) - user.save() - - wal = WalletMasterKeys.objects.create( - user=user, encrypted_mnemonic=encrypted_mnemonic - ) - - user = authenticate(username=username, password=password) - init_login(request, user) - - return http.JsonResponse({ - 'wallet_settings': wal.get_settings(), - 'exchange_rates': get_rates(wal.display_fiat), - }) + if request.method == "POST": + encrypted_mnemonic = request.POST['encrypted_mnemonic'] + password = request.POST['password'] + username = request.POST['username'] + + try: + user = User.objects.create( + username=username, + email=request.POST.get('email', ''), + ) + user.set_password(password) + user.save() + except IntegrityError: + # The user already exists + return redirect("wallet") + + wal = WalletMasterKeys.objects.create( + user=user, encrypted_mnemonic=encrypted_mnemonic + ) + + user = authenticate(username=username, password=password) + init_login(request, user) + + return http.JsonResponse({ + 'wallet_settings': wal.get_settings(), + 'exchange_rates': get_rates(wal.display_fiat), + }) + else: + return redirect("wallet") def login(request): @@ -93,30 +104,37 @@ def login(request): Authenticate the user. On failed attempts, record the event, and limit 5 failed attempts every 15 minutes. """ - username = request.POST['username'] - password = request.POST['password'] - - fithteen_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes=15) - tries = FailedLogin.objects.filter(username=username, time__gt=fithteen_minutes_ago) - try_count = tries.count() - - if try_count < 5: - user = authenticate(username=username, password=password) - - if user and user.is_authenticated(): - init_login(request, user) - wal = WalletMasterKeys.objects.get(user=request.user) - - return http.JsonResponse({ - 'encrypted_mnemonic': wal.encrypted_mnemonic, - 'wallet_settings': wal.get_settings(), - 'exchange_rates': get_rates(wal.display_fiat), - }) - else: - FailedLogin.objects.create(username=username) - tries_left = 5 - try_count - return http.JsonResponse({"tries_left": tries_left}, status=401) - - time_of_next_try = tries.latest().time + datetime.timedelta(minutes=15) - minutes_to_wait = (time_of_next_try - timezone.now()).total_seconds() / 60.0 - return http.JsonResponse({"login_timeout": "Try again in %.1f minutes." % minutes_to_wait}, status=401) + if request.method == "POST": + username = request.POST['username'] + password = request.POST['password'] + + fithteen_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes=15) + tries = FailedLogin.objects.filter( + username=username, time__gt=fithteen_minutes_ago) + try_count = tries.count() + + if try_count < 5: + user = authenticate(username=username, password=password) + + if user and user.is_authenticated(): + init_login(request, user) + wal = WalletMasterKeys.objects.get(user=request.user) + + return http.JsonResponse({ + 'encrypted_mnemonic': wal.encrypted_mnemonic, + 'wallet_settings': wal.get_settings(), + 'exchange_rates': get_rates(wal.display_fiat), + }) + else: + FailedLogin.objects.create(username=username) + tries_left = 5 - try_count + return http.JsonResponse({"tries_left": tries_left}, status=401) + + time_of_next_try = tries.latest().time + datetime.timedelta(minutes=15) + minutes_to_wait = (time_of_next_try - timezone.now() + ).total_seconds() / 60.0 + return http.JsonResponse( + {"login_timeout": "Try again in %.1f minutes." % + minutes_to_wait}, status=401) + else: + return redirect("wallet")