import random import string from datetime import timedelta # import service_clients from django.conf import settings from django.contrib.auth.base_user import BaseUserManager from django.contrib.auth.models import AbstractUser from django.contrib.auth.validators import UnicodeUsernameValidator from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ import uuid from .provinces_and_cities import state # from .tasks import send_notification from apps.users.constans import MAX_OTP_TRY, DEVELOPMENT_PHONE_NUMBERS class UserManager(BaseUserManager): use_in_migrations = True def _create_user(self, phone_number=None, username=None, email=None, password=None, **extra_fields): if not phone_number and not email and not username: raise ValueError('The given phone_number or email must be set') email = self.normalize_email(email) username = self.model.normalize_username(username) # TODO: validate phone number user = self.model(phone_number=phone_number, username=username, email=email, **extra_fields) user.set_password(password) user.set_otp() user.date_joined = timezone.now() user.save(using=self._db) return user def create_user(self, phone_number=None, username=None, email=None, password=None, **extra_fields): extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False) return self._create_user(phone_number=phone_number, username=username, email=email, password=password, **extra_fields) def create_superuser(self, username, email, password, **extra_fields): if not username: raise ValueError('The given username must be set') extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) if extra_fields.get('is_staff') is not True: raise ValueError('Superuser must have is_staff=True.') if extra_fields.get('is_superuser') is not True: raise ValueError('Superuser must have is_superuser=True.') return self._create_user(username=username, email=email, password=password, **extra_fields) class User(AbstractUser): uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True) name = models.CharField(_('name'), max_length=30, blank=True) first_name = models.CharField(_('first name'), max_length=30, blank=True) last_name = models.CharField(_('last name'), max_length=150, blank=True) email = models.EmailField(_('email address'), blank=True) email_verified = models.BooleanField(null=True) # country = models.CharField(_('country'), max_length=50, blank=True, null=True) # province = models.CharField(_('province'), max_length=50, blank=True, null=True) # city = models.CharField(_('city'), max_length=50, blank=True, null=True) # postal_code = models.CharField(_('postal_code'), max_length=20, blank=True, null=True) # address = models.TextField(_('address'), null=True, blank=True) province = models.IntegerField(verbose_name=_("Province"), blank=True, null=True) city = models.IntegerField(verbose_name=_("City"), blank=True, null=True) postal_code = models.CharField(_('postal_code'), max_length=20, blank=True, null=True) address = models.TextField(_('address'), null=True, blank=True) is_staff = models.BooleanField( _('staff status'), default=False, help_text=_('Designates whether the user can log into this admin site.'), ) is_active = models.BooleanField( _('active'), default=True, help_text=_( 'Designates whether this user should be treated as active. ' 'Unselect this instead of deleting accounts.' ), ) password = models.CharField(_('password'), max_length=128) username_validator = UnicodeUsernameValidator() username = models.CharField( _('username'), max_length=150, unique=True, help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'), validators=[username_validator], error_messages={ 'unique': _("A user with that username already exists."), }, blank=True, null=True ) phone_number = models.CharField(_('phone number'), max_length=30, blank=True, null=True, unique=True) otp = models.CharField(_('otp'), max_length=6, blank=True, null=True) otp_expire = models.DateTimeField(_('otp expire'), blank=True, null=True) otp_try = models.IntegerField(_('otp try'), blank=True, null=True, default=0) ott = models.CharField(_('one time token'), max_length=36, blank=True, null=True) ott_expire = models.DateTimeField(_('otp expire'), blank=True, null=True) last_checkout_request = models.DateTimeField(_('otp expire'), max_length=30, blank=True, null=True) balance = models.BigIntegerField(_('balance'), default=0) iban = models.CharField(null=True, max_length=30, blank=True) iban_verified = models.BooleanField(null=True) avatar = models.ImageField(upload_to='avatars', null=True, blank=True) last_update = models.DateTimeField(_('last update'), max_length=30, blank=True, null=True, auto_now=True) last_login = models.DateTimeField(_('last login'), blank=True, null=True) date_joined = models.DateTimeField(_('date joined'), default=timezone.now) objects = UserManager() @property def city_name(self): return state.get_city_by_id(self.city) @property def province_name(self): return state.get_province_by_id(self.province) def set_otp(self): if settings.DEBUG or self.phone_number in DEVELOPMENT_PHONE_NUMBERS: self.otp = '12345' else: self.otp = ''.join(random.choice('0123456789') for _ in range(5)) self.otp_expire = timezone.now() + timedelta(minutes=5) self.otp_try = 0 def set_ott(self): self.ott = ''.join(random.choice(string.ascii_letters+string.digits) for _ in range(32)) # self.ott = ''.join(random.choice(string.ascii_letters+string.digits+string.punctuation) for _ in range(32)) self.ott_expire = timezone.now() + timedelta(seconds=300) def otp_is_valid(self): return bool(self.otp and timezone.now() <= self.otp_expire) def ott_is_valid(self): return bool(self.ott and timezone.now() <= self.ott_expire) def check_otp(self, otp): if self.otp_try <= MAX_OTP_TRY: result = otp and self.otp == otp and self.otp_is_valid() if result: self.otp = None self.date_joined = timezone.now() else: self.otp_try += 1 else: self.otp = None result = False self.save() return result def check_ott(self, ott:str): ott = ott.strip() if ott and self.ott == ott and self.ott_is_valid(): self.ott = None self.date_joined = timezone.now() result = True else: self.ott = None result = False self.save() return result def check_auth(self, field, value): result = False if field == 'otp': result = self.check_otp(value) elif field == 'password': result = self.check_password(value) elif field == 'ott': result = self.check_ott(value) if result: self.last_login = timezone.now() self.save() return result def send_otp(self): print(self.otp) self.notify(body=str(self.otp), title='OTP') def notify(self, body, title=None, notification_type='sms'): return # TODO: enable celery send_notification(phone_number=self.phone_number, title=title, body=body, user_uuid=self.uuid, notification_type=1, ) def __str__(self): return self.name or self.get_full_name() or self.username or self.phone_number or self.email or _('no name') def create_user_in_introspection(token, content): # content = { # "active": True, # "scope": "read write email", # "client_id": "J8NFmU4tJVgDxKaJFmXTWvaHO", # "username": "aaronpk", # "exp": 1437275311 # } user_client = service_clients.Client(access_token=token, scopes=content.get('scope', '').split(), expires_in=100) user_account = user_client.accounts.account() if user_account.get('uuid'): content.update(user_account) # content = {'active': True, 'scope': 'ipg.payment:submit accounts.account:retrieve', 'exp': 1602619137, # 'client_id': 'bd2hEGXytrqMjbFnplRyHJTeoW1vwKzCZJtH6ro0', 'username': '', # 'uuid': '94255117-117c-4a9f-af50-35a6c47503ac', 'email': '', 'phone_number': '+989106853582', # 'first_name': '', 'last_name': '', 'name': '', 'balance': 0, # 'avatar': 'http://accounts.gooyal.com/media/avatars/1691899.jpg', 'iban': '', 'iban_verified': None} user = User.objects.filter(uuid=content['uuid']).first() if user: pass else: user = User.objects.create_user( **{User.USERNAME_FIELD: content["username"]}, uuid=content['uuid'], email=content['email'], phone_number=content['phone_number'], first_name=content['first_name'], last_name=content['last_name'], name=content['name'], avatar=content['avatar'] ) return user, content