示例#1
0
class UserRight(models.Model):
    """
    用户可读资源权限表(记录加密资源可读性)
    """
    user = models.ForeignKey(
        'User.User',
        on_delete=models.CASCADE,
    )
    res = models.ForeignKey(
        'Resource.Resource',
        on_delete=models.CASCADE,
    )
    verify_time = models.FloatField(default=0, )

    @classmethod
    def update(cls, user: User, res: Resource):
        try:
            right = cls.get_right(user, res)
            right.verify_time = datetime.datetime.now().timestamp()
            right.save()
        except E as e:
            if e.eis(ResourceError.RESOURCE_NOT_FOUND):
                try:
                    right = cls(
                        user=user,
                        res=res,
                        verify_time=datetime.datetime.now().timestamp())
                    right.save()
                except Exception:
                    raise ResourceError.CREATE_RIGHT
            else:
                return e
        return right

    @classmethod
    def get_right(cls, user, res):
        try:
            right = cls.objects.get(user=user, res=res)
        except cls.DoesNotExist:
            raise ResourceError.RIGHT_NOT_FOUND
        return right

    @classmethod
    def verify(cls, user: User, res: Resource):
        if not user:
            return False
        try:
            right = cls.get_right(user, res)
        except E:
            return False
        if right.verify_time > res.vk_change_time:
            return True
        else:
            right.delete()
            return False
示例#2
0
class User(models.Model):
    """
    用户类
    根超级用户id=1
    """
    ROOT_ID = 1

    username = models.CharField(
        max_length=32,
        min_length=3,
        unique=True,
        blank=True,
        null=True,
        default=None,
    )
    password = models.CharField(
        max_length=32,
        min_length=6,
    )
    salt = models.CharField(
        max_length=10,
        default=None,
    )
    pwd_change_time = models.FloatField(
        null=True,
        blank=True,
        default=0,
    )
    inviter = models.ForeignKey('User.User',
                                on_delete=models.CASCADE,
                                default=None,
                                null=True)
    invite_code = models.CharField(
        default=None,
        max_length=255,
        blank=True,
        null=True,
    )
    enter_room = models.BooleanField(default=False)

    def is_ancestor(self, user: '******'):
        while user:
            if self == user:
                return True
            user = user.inviter
        return False

    @staticmethod
    def _valid_username(username):
        """验证用户名合法"""
        if username[0] not in string.ascii_lowercase + string.ascii_uppercase:
            raise UserError.INVALID_USERNAME_FIRST
        valid_chars = '^[A-Za-z0-9_]{3,32}$'
        if re.match(valid_chars, username) is None:
            raise UserError.INVALID_USERNAME

    @staticmethod
    def _valid_password(password):
        """验证密码合法"""
        valid_chars = '^[A-Za-z0-9!@#$%^&*()_+-=,.?;:]{6,16}$'
        if re.match(valid_chars, password) is None:
            raise UserError.INVALID_PASSWORD

    @staticmethod
    def hash_password(raw_password, salt=None):
        if not salt:
            salt = get_random_string(length=6)
        hash_password = User._hash(raw_password + salt)
        return salt, hash_password

    @classmethod
    def exist_with_username(cls, username):
        try:
            cls.objects.get(username=username)
        except cls.DoesNotExist:
            return
        raise UserError.USERNAME_EXIST

    @classmethod
    def inviterjsonArr(cls, data):
        rData = []
        for item in data:
            item.__dict__.pop("_state")
            rData.append(item.__dict__)
        return rData

    @classmethod
    def return_inviter(cls, invite_code):
        try:

            cls.objects.get(invite_code=invite_code)
        except cls.DoesNotExist:
            raise UserError.NOT_FOUND_COD
        else:
            user = User.objects.get(invite_code=invite_code)
            print(user.username)
            return user

    @classmethod
    def create_invite(cls, username, password, invite_code):
        """ 创建用户(有邀请码)

                :param username: 用户名
                :param password: 密码
                :param invite_code: 邀请码
                :return: Ret对象,错误返回错误代码,成功返回用户对象
                """
        salt, hashed_password = User.hash_password(password)
        User.exist_with_username(username)
        # User.exist_with_invitecode(invite_code)
        # print(invite_code)
        inviter = User.return_inviter(invite_code)
        # print(inviter.username)
        try:
            user = cls(username=username,
                       password=hashed_password,
                       salt=salt,
                       inviter=inviter,
                       invite_code=username + "666")
            user.save()
        except Exception:
            raise UserError.CREATE_USER
        return user

    @classmethod
    def create(cls, username, password):
        """ 创建用户

        :param username: 用户名
        :param password: 密码
        :param invite_code: 邀请码
        :return: Ret对象,错误返回错误代码,成功返回用户对象
        """
        cls.validator(locals())

        salt, hashed_password = User.hash_password(password)

        User.exist_with_username(username)
        try:
            user = cls(
                username=username,
                password=hashed_password,
                salt=salt,
            )
            user.save()
        except Exception:
            raise UserError.CREATE_USER
        return user

    def change_password(self, password, old_password):
        """修改密码"""
        self.validator(locals())

        if self.password != User._hash(old_password):
            raise UserError.PASSWORD
        self.salt, self.password = User.hash_password(password)
        import datetime
        self.pwd_change_time = datetime.datetime.now().timestamp()
        self.save()

    @staticmethod
    def _hash(s):
        import hashlib
        md5_ = hashlib.md5()
        md5_.update(s.encode())
        return md5_.hexdigest()

    @staticmethod
    def get_user_by_username(username):
        """根据用户名获取用户对象"""
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise UserError.NOT_FOUND_USER
        return user

    @staticmethod
    def get_user_by_id(user_id):
        """根据用户ID获取用户对象"""
        try:
            user = User.objects.get(pk=user_id)
        except User.DoesNotExist:
            raise UserError.NOT_FOUND_USER
        return user

    def entered_room(self):
        self.enter_room = True
        self.save()

    def leave_room(self):
        print("######leave the room")
        self.enter_room = False
        self.save()

    @classmethod
    def authenticate(cls, username, password):
        """验证用户名和密码是否匹配"""
        cls.validator(locals())

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist as err:
            raise UserError.NOT_FOUND_USER

        salt, hashed_password = User.hash_password(password, user.salt)
        if hashed_password == user.password:
            return user
        raise UserError.PASSWORD

    def is_beinviter(self, inviter):
        if self.username == inviter:
            return True
        else:
            inviters = User.objects.filter(inviter__exact=self.username)
            if len(inviters) > 0:
                for invite in inviters:
                    # print(inviter.username)
                    return invite.is_beinviter(inviter)
            else:
                return False

    def d(self):
        return self.dictor('pk->uid', 'username', 'inviter', 'enter_room')

    def d_invite(self):
        return self.dictor('pk->id', 'username')

    def d_username(self):
        return self.dictor('username')

    def _readable_inviter(self):
        if self.inviter:
            return self.inviter.d_base()

    def d_base(self):
        return self.dictor('pk->id', 'username')
示例#3
0
文件: models.py 项目: 1997xhw/Akiswan
class User(models.Model):
    """
    用户类
    根超级用户id=1
    """
    ROOT_ID = 1

    username = models.CharField(
        max_length=32,
        min_length=3,
        unique=True,
        blank=True,
        null=True,
        default=None,
    )
    password = models.CharField(
        max_length=32,
        min_length=6,
    )
    salt = models.CharField(
        max_length=10,
        default=None,
    )
    pwd_change_time = models.FloatField(
        null=True,
        blank=True,
        default=0,
    )
    nickname = models.CharField(
        max_length=10,
        default=None,
    )
    phone = models.CharField(
        default=None,
        unique=True,
        max_length=20,
    )
    meat_quantity = models.IntegerField(
        # 已发布任务数目
        default=0, )

    def is_ancestor(self, user: '******'):
        while user:
            if self == user:
                return True
            user = user.inviter
        return False

    @staticmethod
    def _valid_username(username):
        """验证用户名合法"""
        if username[0] not in string.ascii_lowercase + string.ascii_uppercase:
            raise UserError.INVALID_USERNAME_FIRST
        valid_chars = '^[A-Za-z0-9_]{3,32}$'
        if re.match(valid_chars, username) is None:
            raise UserError.INVALID_USERNAME

    @staticmethod
    def _valid_password(password):
        """验证密码合法"""
        valid_chars = '^[A-Za-z0-9!@#$%^&*()_+-=,.?;:]{6,16}$'
        if re.match(valid_chars, password) is None:
            raise UserError.INVALID_PASSWORD

    @staticmethod
    def hash_password(raw_password, salt=None):
        if not salt:
            salt = get_random_string(length=6)
        hash_password = User._hash(raw_password + salt)
        return salt, hash_password

    @classmethod
    def exist_with_username(cls, username):
        try:
            cls.objects.get(username=username)
        except cls.DoesNotExist:
            return
        raise UserError.USERNAME_EXIST

    @classmethod
    def get_by_phone(cls, phone):
        """根据手机号获取用户对象"""
        try:
            cls.objects.get(phone=phone)
            raise UserError.PHONE_REGISTERED
        except cls.DoesNotExist:
            pass

    @classmethod
    def inviterjsonArr(cls, data):
        rData = []
        for item in data:
            item.__dict__.pop("_state")
            rData.append(item.__dict__)
        return rData

    @classmethod
    def create(cls, phone, username, password, nickname):
        """ 创建用户
        :param phone: 手机号
        :param username: 用户名
        :param password: 密码
        :param nickname: 昵称
        :return: Ret对象,错误返回错误代码,成功返回用户对象
        """
        cls.validator(locals())

        salt, hashed_password = User.hash_password(password)

        User.exist_with_username(username)
        try:
            user = cls(username=username,
                       password=hashed_password,
                       salt=salt,
                       nickname=nickname,
                       phone=phone)
            user.save()
        except Exception as err:
            raise UserError.CREATE_USER(debug_message=err)
        return user

    def add_meat_quantity(self):
        try:
            self.meat_quantity = self.meat_quantity + 1
            self.save()
        except Exception:
            raise UserError.ADD_MEAT_QUANTITY

    def reduce_meat_quantity(self):
        try:
            self.meat_quantity = self.meat_quantity - 1
            self.save()
        except Exception:
            raise UserError.ADD_MEAT_QUANTITY

    def change_password(self, password, old_password):
        """修改密码"""
        self.validator(locals())

        if self.password != User._hash(old_password):
            raise UserError.PASSWORD
        self.salt, self.password = User.hash_password(password)
        import datetime
        self.pwd_change_time = datetime.datetime.now().timestamp()
        self.save()

    @staticmethod
    def _hash(s):
        import hashlib
        md5_ = hashlib.md5()
        md5_.update(s.encode())
        return md5_.hexdigest()

    @staticmethod
    def get_user_by_username(username):
        """根据用户名获取用户对象"""
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise UserError.NOT_FOUND_USER
        return user

    @staticmethod
    def get_user_by_id(user_id):
        """根据用户ID获取用户对象"""
        try:
            user = User.objects.get(pk=user_id)
        except User.DoesNotExist:
            raise UserError.NOT_FOUND_USER
        return user

    @classmethod
    def authenticate(cls, username, password):
        """验证用户名和密码是否匹配"""
        cls.validator(locals())

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist as err:
            raise UserError.NOT_FOUND_USER

        salt, hashed_password = User.hash_password(password, user.salt)
        if hashed_password == user.password:
            return user
        raise UserError.PASSWORD

    def change_talked(self):
        print(bool(1 - self.talked))
        self.talked = bool(1 - self.talked)
        self.save()

    def d(self):
        return self.dictor('pk->uid', 'username', 'nickname')

    def d_base(self):
        return self.dictor('pk->id', 'username')
示例#4
0
class Resource(models.Model):
    """
    资源类
    根资源文件夹id=1
    一旦新增用户,就在根目录创建一个属于新增用户的文件夹
    """
    ROOT_ID = 1

    rname = models.CharField(
        verbose_name='resource name',
        max_length=256,
    )
    rtype = models.IntegerField(
        verbose_name='file or folder',
        choices=RtypeChoice.list(),
    )
    rsize = models.IntegerField(default=0, )
    sub_type = models.IntegerField(
        verbose_name='sub type',
        choices=StypeChoice.list(),
        default=StypeChoice.FOLDER.value,
    )
    mime = models.CharField(
        verbose_name='资源类型',
        null=True,
        default=True,
        max_length=100,
    )
    description = models.TextField(
        verbose_name='description in Markdown',
        null=True,
        blank=True,
        default=None,
    )
    cover = models.CharField(
        null=True,
        blank=True,
        default=None,
        max_length=1024,
    )
    owner = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
    )
    parent = models.ForeignKey(
        'Resource',
        null=True,
        blank=True,
        default=0,
        on_delete=models.CASCADE,
    )
    dlpath = models.CharField(
        verbose_name='download relative path to res.6-79.cn',
        max_length=1024,
        default=None,
        null=True,
        blank=True,
    )
    status = models.IntegerField(
        choices=StatusChoice.list(),
        verbose_name='加密状态 0公开 1仅自己可见 2需要密码',
        default=StatusChoice.PUBLIC.value,
    )
    visit_key = models.CharField(
        max_length=16,
        min_length=3,
        verbose_name='当status为2时有效',
    )
    vk_change_time = models.FloatField(
        null=True,
        blank=True,
        default=0,
    )
    create_time = models.DateTimeField()
    dlcount = models.IntegerField(
        verbose_name='download number',
        default=0,
    )
    res_str_id = models.CharField(
        verbose_name='唯一随机资源ID,弃用res_id',
        default=None,
        null=True,
        blank=True,
        max_length=6,
        unique=True,
    )
    right_bubble = models.NullBooleanField(
        verbose_name='读取权限向上冒泡',
        default=True,
    )
    cover_type = models.IntegerField(
        choices=CoverChoice.list(),
        verbose_name='封面类型 0 上传图片 1 与父资源相同 2 与指定资源相同 3 外部URI链接',
        default=CoverChoice.RANDOM.value,
        null=0,
        blank=0,
    )

    @classmethod
    def get_unique_id(cls):
        while True:
            res_str_id = get_random_string(length=6)
            try:
                cls.get_by_id(res_str_id)
            except E as ret:
                if ret.eis(ResourceError.RESOURCE_NOT_FOUND):
                    return res_str_id

    @staticmethod
    def _valid_rname(rname):
        """验证rname属性"""
        invalid_chars = '\\/*:\'"|<>?'
        for char in invalid_chars:
            if char in rname:
                raise ResourceError.INVALID_RNAME

    @staticmethod
    def _valid_res_parent(parent):
        """验证parent属性"""
        if not isinstance(parent, Resource):
            raise BaseError.STRANGE
        if parent.rtype != RtypeChoice.FOLDER.value:
            raise ResourceError.FILE_PARENT

    @classmethod
    def create_abstract(cls, rname, rtype, desc, user, parent, dlpath, rsize,
                        sub_type, mime):
        crt_time = datetime.datetime.now()

        return cls(
            rname=rname,
            rtype=rtype,
            mime=mime,
            description=desc,
            cover=None,
            cover_type=CoverChoice.SELF.value if sub_type
            == StypeChoice.IMAGE.value else CoverChoice.RANDOM.value,
            owner=user,
            parent=parent,
            dlpath=dlpath,
            status=StatusChoice.PRIVATE.value,
            visit_key=get_random_string(length=4),
            create_time=crt_time,
            vk_change_time=crt_time.timestamp(),
            rsize=rsize,
            sub_type=sub_type,
            res_str_id=cls.get_unique_id(),
            dlcount=0,
            right_bubble=True,
        )

    @classmethod
    def create_file(cls, rname, user, res_parent, dlpath, rsize, sub_type,
                    mime):
        """ 创建文件对象

        :param mime: 七牛返回的资源类型
        :param rname: 文件名
        :param user: 所属用户
        :param res_parent: 所属目录
        :param dlpath: 七牛存储的key
        :param rsize: 文件大小
        :param sub_type: 文件分类
        :return: Ret对象,错误返回错误代码,成功返回文件对象
        """
        try:
            res = cls.create_abstract(
                rname=rname,
                rtype=RtypeChoice.FILE.value,
                desc=None,
                user=user,
                parent=res_parent,
                dlpath=dlpath,
                rsize=rsize,
                sub_type=sub_type,
                mime=mime,
            )
            res.save()
        except Exception:
            raise ResourceError.CREATE_FILE
        return res

    @classmethod
    def create_folder(cls, rname, user, res_parent, desc=None):
        """ 创建文件夹对象

        :param rname: 文件夹名
        :param user: 所属用户
        :param res_parent: 所属目录
        :param desc: 描述说明
        :return: Ret对象,错误返回错误代码,成功返回文件夹对象
        """
        try:
            res = cls.create_abstract(
                rname=rname,
                rtype=RtypeChoice.FOLDER.value,
                desc=desc,
                user=user,
                parent=res_parent,
                dlpath=None,
                rsize=0,
                sub_type=StypeChoice.FOLDER.value,
                mime=None,
            )
            res.save()
        except Exception:
            raise ResourceError.CREATE_FOLDER
        return res

    @classmethod
    def create_link(cls, rname, user, res_parent, dlpath):
        """ 创建链接对象

        :param rname: 链接名称
        :param user: 所属用户
        :param res_parent: 所在目录
        :param dlpath: 链接地址
        :return: Ret对象,错误返回错误代码,成功返回链接对象
        """
        try:
            res = cls.create_abstract(
                rname=rname,
                rtype=RtypeChoice.LINK,
                desc=None,
                user=user,
                parent=res_parent,
                dlpath=dlpath,
                rsize=0,
                sub_type=StypeChoice.LINK.value,
                mime=None,
            )
            res.save()
        except Exception:
            raise ResourceError.CREATE_LINK
        return res

    """
    查询方法
    """

    @classmethod
    def get_by_id(cls, res_str_id):
        try:
            res = cls.objects.get(res_str_id=res_str_id)
        except cls.DoesNotExist:
            raise ResourceError.RESOURCE_NOT_FOUND
        return res

    @classmethod
    def get_by_pk(cls, res_id):
        """根据资源id获取资源对象"""
        try:
            res = cls.objects.get(pk=res_id)
        except cls.DoesNotExist:
            raise ResourceError.RESOURCE_NOT_FOUND
        return res

    def belong(self, user):
        """判断资源是否属于用户"""
        return self.owner.pk == user.pk

    def is_home(self):
        return self.parent.pk == Resource.ROOT_ID

    def get_cover_urls(self):
        """获取封面链接"""
        res = self
        cover = None
        while res.pk != Resource.ROOT_ID:
            if res.cover_type == CoverChoice.PARENT.value:
                res = res.parent
            elif res.cover_type == CoverChoice.RESOURCE.value:
                try:
                    res = Resource.get_by_id(res.cover)
                except E:
                    return None, None
                if not res.belong(self.owner):
                    return None, None
            else:
                cover = res.cover
                break
        if res.cover_type == CoverChoice.SELF.value:
            if res.sub_type == StypeChoice.IMAGE.value:
                from Base.qn_manager import qn_res_manager
                return (qn_res_manager.get_resource_url(res.dlpath),
                        qn_res_manager.get_resource_url("%s-small" %
                                                        res.dlpath))
        if cover is None:
            return None, None
        if res.cover_type == CoverChoice.UPLOAD.value:
            from Base.qn_manager import qn_res_manager
            return (qn_res_manager.get_resource_url(cover),
                    qn_res_manager.get_resource_url("%s-small" % cover))
        else:
            return cover, cover

    """
    字典方法
    """

    def _readable_owner(self):
        return self.owner.d()

    def _readable_create_time(self):
        return self.create_time.timestamp()

    def _readable_visit_key(self):
        return self.visit_key if self.status == StatusChoice.PROTECT.value else None

    def _readable_is_home(self):
        return self.is_home()

    def _readable_secure_env(self):
        if self.pk == Resource.ROOT_ID or \
                not self.right_bubble or \
                self.status == StatusChoice.PUBLIC.value:
            return True
        res = self.parent
        while res.pk != Resource.ROOT_ID:
            if res.status == StatusChoice.PUBLIC.value:
                return res.rname
            if not res.right_bubble:
                break
            res = res.parent
        return True

    def _readable_raw_cover(self):
        return self.cover

    def _readable_parent_str_id(self):
        return self.parent.res_str_id

    def d(self):
        dict_ = self.dictify('res_str_id', 'rname', 'rtype', 'rsize',
                             'sub_type', 'description', 'cover_type', 'owner',
                             'parent_str_id', 'status', 'create_time',
                             'dlcount', 'visit_key', 'is_home', 'right_bubble',
                             'secure_env', 'raw_cover')
        cover_urls = self.get_cover_urls()
        dict_.update(dict(
            cover=cover_urls[0],
            cover_small=cover_urls[1],
        ))
        return dict_

    def d_base(self):
        dict_ = self.dictify('status', 'is_home', 'owner', 'create_time',
                             'right_bubble')
        cover_urls = self.get_cover_urls()
        dict_.update(dict(
            cover=cover_urls[0],
            cover_small=cover_urls[1],
        ))
        return dict_

    def d_child(self):
        dict_ = self.dictify('res_str_id', 'rname', 'rtype', 'status',
                             'create_time', 'sub_type', 'dlcount')
        cover_urls = self.get_cover_urls()
        dict_.update(dict(cover_small=cover_urls[1], ))
        return dict_

    def d_layer(self):
        child_list = Resource.objects.filter(parent=self).dict(
            Resource.d_child)
        return dict(
            info=self.d(),
            child_list=child_list,
        )

    def d_child_selector(self):
        return self.dictify('res_str_id', 'rname', 'rtype', 'sub_type')

    def d_selector_layer(self):
        child_list = Resource.objects.filter(parent=self).dict(
            Resource.d_child_selector)
        info = self.dictify('is_home', 'res_str_id', 'rname', 'parent_str_id')
        return dict(
            info=info,
            child_list=child_list,
        )

    """
    查询方法
    """

    @classmethod
    def get_root_folder(cls, user):
        """获取当前用户的根目录"""
        try:
            res = cls.objects.get(owner=user,
                                  parent=1,
                                  rtype=RtypeChoice.FOLDER.value)
        except cls.DoesNotExist:
            raise ResourceError.GET_ROOT_FOLDER
        return res

    def readable(self, user, visit_key):
        """判断当前资源是否被当前用户可读"""
        res = self
        while res.pk != Resource.ROOT_ID:
            if res.owner == user or res.status == StatusChoice.PUBLIC.value:
                return True
            if res.status == StatusChoice.PROTECT.value and res.visit_key == visit_key:
                if user:
                    UserRight.update(user, res)
                return True
            if res.status == StatusChoice.PROTECT.value and UserRight.verify(
                    user, res):
                return True
            if not res.right_bubble:
                break
            res = res.parent
            visit_key = None
        return False

    def get_dl_url(self):
        """获取当前资源的下载链接"""
        self.dlcount += 1
        self.save()
        if self.rtype == RtypeChoice.LINK:
            return self.dlpath
        from Base.qn_manager import qn_res_manager
        return qn_res_manager.get_resource_url(self.dlpath)

    def get_visit_key(self):
        """获取当前资源的访问密码"""
        if self.status == StatusChoice.PROTECT.value:
            return self.visit_key
        return None

    """
    修改方法
    """

    def modify_rname(self, rname):
        key = self.dlpath
        new_key = '%s/%s' % (key[:key.rfind('/')], rname)
        from Base.qn_manager import qn_res_manager
        qn_res_manager.move_res(key, new_key)
        self.rname = rname
        self.dlpath = new_key
        self.save()

    def modify_info(self, rname, description, status, visit_key, right_bubble,
                    parent):
        """ 修改资源属性

        :param rname: 资源名称
        :param description: 资源介绍
        :param status: 资源分享类型(公开、私有、加密)
        :param visit_key: 资源加密密钥
        :param right_bubble: 资源读取权限是否向上查询
        :param parent: 移动后的新父目录
        :return: Ret对象,错误返回错误代码,成功返回资源对象
        """
        if rname is None:
            rname = self.rname
        if description is None:
            description = self.description or ''
        if status is None:
            status = self.status
        if visit_key is None:
            visit_key = self.visit_key
        if right_bubble is None:
            right_bubble = self.right_bubble
        if parent is None:
            parent = self.parent

        if self.rname != rname:
            if self.rtype == RtypeChoice.FILE.value:
                self.modify_rname(rname)
            else:
                self.rname = rname
        self.description = description
        self.status = status
        self.right_bubble = right_bubble
        self.parent = parent
        if status == StatusChoice.PROTECT.value:
            if self.visit_key != visit_key:
                self.visit_key = visit_key
                self.vk_change_time = datetime.datetime.now().timestamp()
        self.save()

    def modify_cover(self, cover, cover_type):
        """修改资源封面"""
        from Base.qn_manager import qn_res_manager
        if self.cover_type == CoverChoice.UPLOAD.value:
            try:
                qn_res_manager.delete_res(self.cover)
            except:
                pass
        self.cover = cover
        self.cover_type = cover_type
        self.save()
        return self

    def is_empty(self):
        """ 资源是否为空(针对文件夹资源) """
        res_list = Resource.objects.filter(parent=self)
        if res_list:
            return False
        return True

    def remove(self):
        """ 删除资源 """
        if self.rtype == RtypeChoice.FOLDER.value:
            if not self.is_empty():
                raise ResourceError.REQUIRE_EMPTY_FOLDER
        from Base.qn_manager import qn_res_manager
        if self.cover and self.cover_type == CoverChoice.UPLOAD.value:
            qn_res_manager.delete_res(self.cover)
            self.cover = None
            self.save()
        if self.rtype == RtypeChoice.FILE.value:
            qn_res_manager.delete_res(self.dlpath)
        self.delete()
示例#5
0
文件: models.py 项目: Jyonn/account
class UserApp(models.Model):
    """用户应用类"""
    user = models.ForeignKey(
        'User.User',
        on_delete=models.CASCADE,
    )
    app = models.ForeignKey(
        'App.App',
        on_delete=models.CASCADE,
    )
    user_app_id = models.CharField(
        max_length=16,
        verbose_name='用户在这个app下的唯一ID',
        unique=True,
    )
    bind = models.BooleanField(
        default=False,
        verbose_name='用户是否绑定应用',
    )
    last_auth_code_time = models.CharField(
        default=None,
        verbose_name='上一次申请auth_code的时间,防止被多次使用',
        max_length=20,
    )
    frequent_score = models.FloatField(
        verbose_name='频繁访问分数,按分值排序为常用应用',
        default=0,
    )
    last_score_changed_time = models.CharField(
        default=None,
        verbose_name='上一次分数变化的时间',
        max_length=20,
    )
    mark = models.PositiveSmallIntegerField(
        verbose_name='此用户的打分,0表示没打分',
        default=0,
    )

    def _readable_rebind(self):
        return float(self.last_auth_code_time) < self.app.field_change_time

    def d(self):
        return self.dictify('bind', 'mark', 'rebind', 'user_app_id')

    @classmethod
    def get_by_user_app(cls, user, app):
        try:
            return cls.objects.get(user=user, app=app)
        except Exception:
            raise AppError.USER_APP_NOT_FOUND

    @classmethod
    def get_by_id(cls, user_app_id, check_bind=False):
        try:
            user_app = cls.objects.get(user_app_id=user_app_id)
        except Exception:
            raise AppError.USER_APP_NOT_FOUND
        if check_bind and not user_app.bind:
            raise AppError.APP_UNBINDED
        return user_app

    @classmethod
    def get_unique_id(cls):
        while True:
            user_app_id = get_random_string(length=8)
            try:
                cls.get_by_id(user_app_id)
            except E as e:
                if e.eis(AppError.USER_APP_NOT_FOUND):
                    return user_app_id

    @classmethod
    def do_bind(cls, user, app):
        if app.is_user_full():
            raise AppError.USER_FULL

        premise_list = app.check_premise(user)
        for premise in premise_list:
            error = E.sid2e[premise['check']['identifier']]
            if not error.ok:
                raise error

        crt_timestamp = datetime.datetime.now().timestamp()

        try:
            user_app = cls.get_by_user_app(user, app)
            user_app.bind = True
            user_app.last_auth_code_time = crt_timestamp
            user_app.frequent_score += 1
            user_app.last_score_changed_time = crt_timestamp
            user_app.save()
        except E as e:
            if e.eis(AppError.USER_APP_NOT_FOUND):
                try:
                    user_app = cls(
                        user=user,
                        app=app,
                        user_app_id=cls.get_unique_id(),
                        bind=True,
                        last_auth_code_time=crt_timestamp,
                        frequent_score=1,
                        last_score_changed_time=crt_timestamp,
                    )
                    user_app.save()
                    user_app.app.user_num += 1
                    user_app.app.save()
                except Exception as err:
                    raise AppError.BIND_USER_APP(debug_message=err)
            else:
                raise e
        return JWT.encrypt(dict(
            user_app_id=user_app.user_app_id,
            type=JWType.AUTH_CODE,
            ctime=crt_timestamp
        ), replace=False, expire_second=5 * 60)

    @classmethod
    def check_bind(cls, user, app):
        try:
            user_app = cls.get_by_user_app(user, app)
            return user_app.bind
        except Exception:
            return False

    @classmethod
    def refresh_frequent_score(cls):
        from Config.models import Config
        crt_date = datetime.datetime.now().date()
        crt_time = datetime.datetime.now().timestamp()
        last_date = Config.get_value_by_key(CI.LAST_RE_FREQ_SCORE_DATE)
        last_date = datetime.datetime.strptime(last_date, '%Y-%m-%d').date()

        if last_date >= crt_date:
            raise AppError.SCORE_REFRESHED

        from OAuth.views import OAUTH_TOKEN_EXPIRE_TIME

        Config.update_value(CI.LAST_RE_FREQ_SCORE_DATE, crt_date.strftime('%Y-%m-%d'))
        for user_app in cls.objects.all():
            if crt_time - float(
                    user_app.last_auth_code_time) > OAUTH_TOKEN_EXPIRE_TIME + 24 * 60 * 60:
                if crt_time - float(user_app.last_score_changed_time) > OAUTH_TOKEN_EXPIRE_TIME:
                    user_app.frequent_score /= 2
                    user_app.last_score_changed_time = crt_time
                    user_app.save()

    def do_mark(self, mark):
        if mark < 1 or mark > 5:
            raise AppError.MARK
        original_mark = self.mark
        self.mark = mark
        self.save()

        mark_list = list(map(int, self.app.mark.split('-')))
        if 5 >= original_mark > 0:
            mark_list[original_mark - 1] -= 1
        mark_list[mark - 1] += 1
        self.app.mark = '-'.join(map(str, mark_list))
        self.app.save()
示例#6
0
文件: models.py 项目: Jyonn/account
class App(models.Model):
    R_USER = '******'
    R_OWNER = 'owner'
    R_NONE = 'none'
    R_LIST = [R_USER, R_OWNER, R_NONE]

    name = models.CharField(
        verbose_name='应用名称',
        max_length=32,
        min_length=2,
        unique=True,
    )
    id = models.CharField(
        verbose_name='应用唯一ID',
        max_length=32,
        primary_key=True,
    )
    secret = models.CharField(
        verbose_name='应用密钥',
        max_length=32,
    )
    redirect_uri = models.URLField(
        verbose_name='应用跳转URI',
        max_length=512,
    )
    test_redirect_uri = models.URLField(
        verbose_name='测试环境下的应用跳转URI',
        max_length=512,
        default=None,
    )
    scopes = models.ManyToManyField(
        'Scope',
        default=None,
    )
    premises = models.ManyToManyField(
        'Premise',
        default=None,
    )
    owner = models.ForeignKey(
        'User.User',
        on_delete=models.CASCADE,
        db_index=True,
    )
    field_change_time = models.FloatField(
        null=True,
        blank=True,
        default=0,
    )
    desc = models.CharField(
        max_length=32,
        default=None,
    )
    logo = models.CharField(
        default=None,
        null=True,
        blank=True,
        max_length=1024,
    )
    mark = models.SlugField(
        default='0-0-0-0-0',
        verbose_name='1-5分评分人数',
    )
    info = models.TextField(
        default=None,
        verbose_name='应用介绍信息',
        null=True,
    )
    user_num = models.IntegerField(
        default=0,
        verbose_name='用户人数',
    )
    create_time = models.DateTimeField(
        default=None,
    )
    max_user_num = models.IntegerField(
        default=0,
        verbose_name='最多注册用户'
    )

    @classmethod
    def get_by_name(cls, name):
        try:
            return cls.objects.get(name=name)
        except cls.DoesNotExist:
            raise AppError.APP_NOT_FOUND

    @classmethod
    def exist_with_name(cls, name):
        try:
            cls.objects.get(name=name)
        except cls.DoesNotExist:
            return
        raise AppError.EXIST_APP_NAME

    @classmethod
    def get_by_id(cls, app_id):
        try:
            return cls.objects.get(pk=app_id)
        except cls.DoesNotExist:
            raise AppError.APP_NOT_FOUND

    @classmethod
    def get_unique_app_id(cls):
        while True:
            app_id = get_random_string(length=8)
            try:
                cls.get_by_id(app_id)
            except E as e:
                if e.eis(AppError.APP_NOT_FOUND):
                    return app_id

    @classmethod
    def create(cls, name, desc, redirect_uri, test_redirect_uri, scopes, premises, owner):
        cls.exist_with_name(name)

        try:
            crt_time = datetime.datetime.now()
            app = cls(
                name=name,
                desc=desc,
                id=cls.get_unique_app_id(),
                secret=get_random_string(length=32),
                redirect_uri=redirect_uri,
                test_redirect_uri=test_redirect_uri,
                owner=owner,
                field_change_time=datetime.datetime.now().timestamp(),
                info=None,
                create_time=crt_time,
            )
            app.save()
            app.scopes.add(*scopes)
            app.premises.add(*premises)
            app.save()
        except Exception as err:
            raise AppError.CREATE_APP(debug_message=err)
        return app

    def modify_test_redirect_uri(self, test_redirect_uri):
        self.test_redirect_uri = test_redirect_uri
        self.save()

    def modify(self, name, desc, info, redirect_uri, scopes, premises, max_user_num):
        """修改应用信息"""
        self.name = name
        self.desc = desc
        self.info = info
        self.redirect_uri = redirect_uri
        self.max_user_num = max_user_num
        for scope in self.scopes.all():
            self.scopes.remove(scope)
        self.scopes.add(*scopes)
        for premise in self.premises.all():
            self.premises.remove(premise)
        self.premises.add(*premises)
        self.field_change_time = datetime.datetime.now().timestamp()
        try:
            self.save()
        except Exception as err:
            raise AppError.MODIFY_APP(debug_message=err)

    def _readable_app_name(self):
        return self.name

    def _readable_app_id(self):
        return self.id

    def _readable_app_desc(self):
        return self.desc

    def _readable_logo(self, small=True):
        if self.logo is None:
            return None
        from Base.qn import qn_public_manager
        key = "%s-small" % self.logo if small else self.logo
        return qn_public_manager.get_resource_url(key)

    def _readable_create_time(self):
        return self.create_time.timestamp()

    def _readable_app_info(self):
        return self.info

    def _readable_owner(self):
        return self.owner.d_base()

    def _readable_mark(self):
        return list(map(int, self.mark.split('-')))

    def mark_as_list(self):
        return self._readable_mark()

    def _readable_scopes(self):
        scopes = self.scopes.all()
        return list(map(lambda s: s.d(), scopes))

    def _readable_premises(self):
        premises = self.premises.all()
        return list(map(lambda p: p.d(), premises))

    def d(self):
        return self.dictify(
            'app_name', 'app_id', 'app_desc', 'app_info', 'user_num', ('logo', False),
            'redirect_uri', 'create_time', 'owner', 'mark', 'scopes', 'premises',
            'test_redirect_uri', 'max_user_num')

    def d_user(self, user):
        dict_ = self.d()
        dict_.update(dict(premises=self.check_premise(user)))
        return dict_

    def d_base(self):
        return self.dictify('app_name', 'app_id', 'logo', 'app_desc', 'user_num', 'create_time')

    def modify_logo(self, logo):
        """修改应用logo"""
        from Base.qn import qn_public_manager
        if self.logo:
            qn_public_manager.delete_res(self.logo)
        self.logo = logo
        self.save()

    def is_user_full(self):
        if self.max_user_num == 0:
            return False
        return self.max_user_num >= self.user_num

    def belong(self, user):
        return self.owner == user

    def authentication(self, app_secret):
        return self.secret == app_secret

    def check_premise(self, user):
        premises = []
        for premise in self.premises.all():
            checker = Premise.get_checker(premise.name)
            if checker and callable(checker):
                try:
                    checker(user)
                    raise BaseError.OK
                except E as e:
                    error = e
            else:
                error = PremiseCheckerError.CHECKER_NOT_FOUND
            p_dict = premise.d()
            p_dict['check'] = dict(
                identifier=error.identifier,
                msg=error.message,
            )
            premises.append(p_dict)
        return premises

    @classmethod
    def list(cls):
        return cls.objects.all().dict(cls.d_base)
示例#7
0
文件: models.py 项目: Jyonn/account
class User(models.Model):
    """
    用户类
    根超级用户id=1
    """
    ROOT_ID = 1

    VERIFY_STATUS_UNVERIFIED = 0
    VERIFY_STATUS_UNDER_AUTO = 1
    VERIFY_STATUS_UNDER_MANUAL = 2
    VERIFY_STATUS_DONE = 3
    VERIFY_STATUS_TUPLE = (
        (VERIFY_STATUS_UNVERIFIED, '没有认证'),
        (VERIFY_STATUS_UNDER_AUTO, '自动认证阶段'),
        (VERIFY_STATUS_UNDER_MANUAL, '人工认证阶段'),
        (VERIFY_STATUS_DONE, '成功认证'),
    )

    VERIFY_CHINA = 0
    VERIFY_ABROAD = 1
    VERIFY_TUPLE = (
        (VERIFY_CHINA, '中国大陆身份证认证'),
        (VERIFY_ABROAD, '其他地区身份认证'),
    )

    user_str_id = models.CharField(
        verbose_name='唯一随机用户ID',
        default=None,
        null=True,
        blank=True,
        max_length=32,
        unique=True,
    )
    qitian = models.CharField(
        default=None,
        unique=True,
        max_length=20,
        min_length=4,
    )
    phone = models.CharField(
        default=None,
        unique=True,
        max_length=20,
    )
    password = models.CharField(
        max_length=32,
        min_length=6,
    )
    salt = models.CharField(
        max_length=10,
        default=None,
    )
    pwd_change_time = models.FloatField(
        null=True,
        blank=True,
        default=0,
    )
    avatar = models.CharField(
        default=None,
        null=True,
        blank=True,
        max_length=1024,
    )
    nickname = models.CharField(
        max_length=10,
        default=None,
    )
    description = models.CharField(
        max_length=20,
        default=None,
        blank=True,
        null=True,
    )
    qitian_modify_time = models.IntegerField(
        verbose_name='齐天号被修改的次数',
        help_text='一般只能修改一次',
        default=0,
    )
    birthday = models.DateField(
        verbose_name='生日',
        default=None,
        null=True,
    )
    email = models.EmailField(
        verbose_name='邮箱',
        default=None,
        null=True,
    )

    verify_status = models.SmallIntegerField(
        verbose_name='是否通过实名认证',
        default=0,
        choices=VERIFY_STATUS_TUPLE,
    )
    real_verify_type = models.SmallIntegerField(
        verbose_name='实名认证类型',
        default=None,
        null=True,
    )
    real_name = models.CharField(
        verbose_name='真实姓名',
        default=None,
        max_length=32,
        null=True,
    )
    male = models.NullBooleanField(
        verbose_name='是否为男性',
        default=None,
        null=True,
    )
    idcard = models.CharField(
        verbose_name='身份证号',
        default=None,
        max_length=18,
        choices=VERIFY_TUPLE,
        null=True,
    )
    card_image_front = models.CharField(
        verbose_name='身份证正面照',
        max_length=1024,
        default=None,
        null=True,
    )
    card_image_back = models.CharField(
        verbose_name='身份证背面照',
        max_length=1024,
        default=None,
        null=True,
    )
    is_dev = models.BooleanField(
        verbose_name='是否开发者',
        default=False,
    )

    @classmethod
    def get_unique_id(cls):
        while True:
            user_str_id = get_random_string(length=6)
            try:
                cls.get_by_str_id(user_str_id)
            except E as e:
                if e.eis(UserError.USER_NOT_FOUND):
                    return user_str_id

    @classmethod
    def get_unique_qitian(cls):
        while True:
            qitian_id = get_random_string(length=8)
            try:
                cls.get_by_qitian(qitian_id)
            except E as e:
                if e.eis(UserError.USER_NOT_FOUND):
                    return qitian_id

    @staticmethod
    def _valid_qitian(qitian):
        """验证齐天号合法"""
        valid_chars = '^[A-Za-z0-9_]{4,20}$'
        if re.match(valid_chars, qitian) is None:
            raise UserError.INVALID_QITIAN

    @staticmethod
    def _valid_password(password):
        """验证密码合法"""
        valid_chars = '^[A-Za-z0-9!@#$%^&*()_+-=,.?;:]{6,16}$'
        if re.match(valid_chars, password) is None:
            raise UserError.INVALID_PASSWORD

    @staticmethod
    def _valid_birthday(birthday):
        """验证生日是否合法"""
        import datetime
        if birthday > datetime.datetime.now().date():
            raise UserError.BIRTHDAY_FORMAT

    @staticmethod
    def hash_password(raw_password, salt=None):
        if not salt:
            salt = get_random_string(length=6)
        hash_password = User._hash(raw_password + salt)
        return salt, hash_password

    @classmethod
    def create(cls, phone, password):
        salt, hashed_password = User.hash_password(password)

        User.exist_with_phone(phone)

        try:
            user = cls(
                qitian=cls.get_unique_qitian(),
                phone=phone,
                password=hashed_password,
                salt=salt,
                avatar=None,
                nickname='',
                description=None,
                qitian_modify_time=0,
                user_str_id=cls.get_unique_id(),
                birthday=None,
                verify_status=cls.VERIFY_STATUS_UNVERIFIED,
                is_dev=False,
            )
            user.save()
        except Exception as err:
            raise UserError.CREATE_USER(debug_message=err)
        return user

    def modify_password(self, password):
        self.salt, self.password = User.hash_password(password)
        import datetime
        self.pwd_change_time = datetime.datetime.now().timestamp()
        self.save()

    def change_password(self, password, old_password):
        """修改密码"""
        if self.password != User._hash(old_password + self.salt):
            raise UserError.PASSWORD
        self.salt, self.password = User.hash_password(password)
        import datetime
        self.pwd_change_time = datetime.datetime.now().timestamp()
        self.save()

    @staticmethod
    def _hash(s):
        from Base.common import md5
        return md5(s)

    @classmethod
    def get_by_str_id(cls, user_str_id):
        try:
            return cls.objects.get(user_str_id=user_str_id)
        except cls.DoesNotExist:
            raise UserError.USER_NOT_FOUND

    @classmethod
    def get_by_phone(cls, phone):
        """根据手机号获取用户对象"""
        try:
            return cls.objects.get(phone=phone)
        except cls.DoesNotExist:
            raise UserError.USER_NOT_FOUND('手机号未注册')

    @classmethod
    def exist_with_phone(cls, phone):
        try:
            cls.objects.get(phone=phone)
        except cls.DoesNotExist:
            return
        raise UserError.PHONE_EXIST

    @classmethod
    def get_by_qitian(cls, qitian_id):
        try:
            return cls.objects.get(qitian=qitian_id)
        except cls.DoesNotExist:
            raise UserError.USER_NOT_FOUND('不存在的齐天号')

    @classmethod
    def exist_with_qitian(cls, qitian_id):
        try:
            cls.objects.get(qitian=qitian_id)
        except cls.DoesNotExist:
            return
        raise UserError.QITIAN_EXIST

    @classmethod
    def get_by_id(cls, user_id):
        """根据用户ID获取用户对象"""
        try:
            return cls.objects.get(pk=user_id)
        except cls.DoesNotExist:
            raise UserError.USER_NOT_FOUND

    def allow_qitian_modify(self):
        return self.qitian_modify_time == 0

    def _readable_avatar(self):
        return self.get_avatar_url()

    def _readable_birthday(self):
        return self.birthday.strftime('%Y-%m-%d') if self.birthday else None

    def _readable_allow_qitian_modify(self):
        return int(self.allow_qitian_modify())

    def d_oauth(self):
        return self.dictify('avatar', 'nickname', 'description')

    def d_base(self):
        return self.dictify('user_str_id', 'avatar', 'nickname', 'description')

    def d(self):
        return self.dictify('birthday', 'user_str_id', 'qitian', 'avatar',
                            'nickname', 'description', 'allow_qitian_modify',
                            'verify_status', 'verify_type', 'is_dev')

    @classmethod
    def authenticate(cls, qitian, phone, password):
        """验证手机号和密码是否匹配"""
        if qitian:
            user = cls.get_by_qitian(qitian)
        else:
            user = cls.get_by_phone(phone)

        salt, hashed_password = User.hash_password(password, user.salt)
        if hashed_password == user.password:
            return user
        raise UserError.PASSWORD

    def get_avatar_url(self, small=True):
        """获取用户头像地址"""
        if self.avatar is None:
            return None
        from Base.qn import qn_public_manager
        key = "%s-small" % self.avatar if small else self.avatar
        return qn_public_manager.get_resource_url(key)

    def get_card_urls(self):
        from Base.qn import qn_res_manager
        front_url = qn_res_manager.get_resource_url(self.card_image_front) \
            if self.card_image_front else None
        back_url = qn_res_manager.get_resource_url(self.card_image_back) \
            if self.card_image_back else None
        return dict(
            front=front_url,
            back=back_url,
        )

    def modify_avatar(self, avatar):
        """修改用户头像"""
        if self.avatar:
            from Base.qn import qn_public_manager
            qn_public_manager.delete_res(self.avatar)
        self.avatar = avatar
        self.save()

    def upload_verify_front(self, card_image_front):
        from Base.qn import qn_res_manager
        if self.card_image_front:
            qn_res_manager.delete_res(self.card_image_front)

        self.card_image_front = card_image_front
        self.save()
        qn_res_manager.get_resource_url(self.card_image_front + '-small')

    def upload_verify_back(self, card_image_back):
        from Base.qn import qn_res_manager
        if self.card_image_back:
            qn_res_manager.delete_res(self.card_image_back)

        self.card_image_back = card_image_back
        self.save()
        qn_res_manager.get_resource_url(self.card_image_back + '-small')

    def modify_info(self, nickname, description, qitian, birthday):
        """修改用户信息"""
        if nickname is None:
            nickname = self.nickname
        if description is None:
            description = self.description
        if qitian is None:
            qitian = self.qitian
        if birthday is None or (self.verify_status and self.real_verify_type
                                == User.VERIFY_CHINA):
            birthday = self.birthday

        if self.allow_qitian_modify():
            if self.qitian != qitian:
                self.exist_with_qitian(qitian)
                self.qitian = qitian
                self.qitian_modify_time += 1
        self.nickname = nickname
        self.description = description
        self.birthday = birthday
        self.save()

    def update_card_info(self, real_name, male, idcard, birthday):
        self.real_name = real_name
        self.male = male
        self.idcard = idcard
        self.birthday = birthday
        try:
            self.save()
        except Exception as err:
            return IDCardError.AUTO_VERIFY_FAILED(debug_message=err)

    def update_verify_status(self, status):
        self.verify_status = status
        self.save()

    def update_verify_type(self, verify_type):
        self.real_verify_type = verify_type
        self.save()

    def developer(self):
        self.is_dev = True
        self.save()