valar 1.2.5__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of valar might be problematic. Click here for more details.
- {valar-1.2.5/src/valar.egg-info → valar-1.3.0}/PKG-INFO +2 -1
- {valar-1.2.5 → valar-1.3.0}/setup.py +2 -2
- {valar-1.2.5 → valar-1.3.0}/src/valar/apps.py +10 -4
- valar-1.3.0/src/valar/auth/Authentication.py +32 -0
- valar-1.3.0/src/valar/auth/Middleware.py +28 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/channels/sender.py +4 -1
- valar-1.3.0/src/valar/classes/valar_response.py +9 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/abstract.py +6 -3
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/defaults/field_keys_default.py +10 -1
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/engine.py +9 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/orm_field.py +4 -6
- valar-1.3.0/src/valar/models/__init__.py +1 -0
- valar-1.3.0/src/valar/models/auth.py +47 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/urls.py +10 -4
- valar-1.3.0/src/valar/views/__init__.py +0 -0
- valar-1.3.0/src/valar/views/auth.py +110 -0
- valar-1.3.0/src/valar/views/password.py +60 -0
- {valar-1.2.5 → valar-1.3.0/src/valar.egg-info}/PKG-INFO +2 -1
- {valar-1.2.5 → valar-1.3.0}/src/valar.egg-info/SOURCES.txt +6 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar.egg-info/requires.txt +1 -0
- valar-1.2.5/src/valar/classes/valar_response.py +0 -8
- valar-1.2.5/src/valar/models/__init__.py +0 -1
- {valar-1.2.5 → valar-1.3.0}/LICENSE +0 -0
- {valar-1.2.5 → valar-1.3.0}/README.md +0 -0
- {valar-1.2.5 → valar-1.3.0}/setup.cfg +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/__init__.py +0 -0
- {valar-1.2.5/src/valar/channels → valar-1.3.0/src/valar/auth}/__init__.py +0 -0
- {valar-1.2.5/src/valar/classes → valar-1.3.0/src/valar/channels}/__init__.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/channels/consumer.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/channels/counter.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/channels/executer.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/channels/mapping.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/channels/views.py +0 -0
- {valar-1.2.5/src/valar/classes/app_mixins → valar-1.3.0/src/valar/classes}/__init__.py +0 -0
- {valar-1.2.5/src/valar/dao/defaults → valar-1.3.0/src/valar/classes/app_mixins}/__init__.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/classes/app_mixins/auto_migration_mixin.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/classes/app_mixins/auto_urlpatterns_mixin.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/classes/singleton_meta.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/classes/valar_minio.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/__init__.py +0 -0
- {valar-1.2.5/src/valar/migrations → valar-1.3.0/src/valar/dao/defaults}/__init__.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/defaults/field_values_default.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/defaults/view_defaults.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/frame.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/meta.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/mon_dao.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/mon_field.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/orm_dao.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/dao/query.py +0 -0
- {valar-1.2.5/src/valar/views → valar-1.3.0/src/valar/migrations}/__init__.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/models/core.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/models/frame.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/models/meta.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/models/test.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/views/file.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/views/handler.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/views/meta.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar/views/rest.py +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar.egg-info/dependency_links.txt +0 -0
- {valar-1.2.5 → valar-1.3.0}/src/valar.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: valar
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: valar for morghulis
|
|
5
5
|
Home-page: https://gitee.com/GRIFFIN120/valar_dev
|
|
6
6
|
Author: LYP
|
|
@@ -16,6 +16,7 @@ Requires-Dist: pandas==2.2.3
|
|
|
16
16
|
Requires-Dist: openpyxl==3.1.5
|
|
17
17
|
Requires-Dist: deepmerge~=2.0
|
|
18
18
|
Requires-Dist: minio==7.2.2
|
|
19
|
+
Requires-Dist: PyJWT~=2.10.1
|
|
19
20
|
Dynamic: author
|
|
20
21
|
Dynamic: author-email
|
|
21
22
|
Dynamic: description
|
|
@@ -12,11 +12,11 @@ requires = [
|
|
|
12
12
|
'openpyxl==3.1.5',
|
|
13
13
|
'deepmerge~=2.0',
|
|
14
14
|
'minio==7.2.2',
|
|
15
|
+
'PyJWT~=2.10.1'
|
|
15
16
|
]
|
|
16
|
-
|
|
17
17
|
setup(
|
|
18
18
|
name="valar", # 包名
|
|
19
|
-
version="1.
|
|
19
|
+
version="1.3.0", # 版本号
|
|
20
20
|
author="LYP", # 作者
|
|
21
21
|
author_email="liuyinpeng@buaa.edu.cn", # 邮箱
|
|
22
22
|
description="valar for morghulis", # 简短描述
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
2
4
|
from django.apps import AppConfig
|
|
3
5
|
|
|
4
6
|
from .classes.app_mixins.auto_migration_mixin import AutoMigrationMixin
|
|
@@ -11,7 +13,11 @@ class ValarConfig(AutoMigrationMixin, AutoUrlPatternsMixin, AppConfig):
|
|
|
11
13
|
|
|
12
14
|
def ready(self):
|
|
13
15
|
if os.environ.get('RUN_MAIN') == 'true':
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
try:
|
|
17
|
+
from .dao.frame import MetaFrame
|
|
18
|
+
getattr(super(), 'set_url', None)()
|
|
19
|
+
getattr(super(), 'auto_migrate', None)()
|
|
20
|
+
MetaFrame()
|
|
21
|
+
except Exception as e:
|
|
22
|
+
traceback.print_exc()
|
|
23
|
+
print('ERROR', e)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import jwt
|
|
2
|
+
from django.conf import settings
|
|
3
|
+
|
|
4
|
+
from src.valar.classes.valar_response import ValarResponse
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def auth_required(view_func):
|
|
8
|
+
def wrapper(request, *args, **kwargs):
|
|
9
|
+
payload, response = validate(request)
|
|
10
|
+
if payload:
|
|
11
|
+
request.user_id = payload["user_id"]
|
|
12
|
+
return view_func(request, *args, **kwargs)
|
|
13
|
+
else:
|
|
14
|
+
return response
|
|
15
|
+
|
|
16
|
+
return wrapper
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate(request):
|
|
20
|
+
auth_header = request.headers.get("Authorization")
|
|
21
|
+
if not auth_header or not auth_header.startswith("Bearer "):
|
|
22
|
+
return None, ValarResponse(False, '请登录系统', 'info', status=401)
|
|
23
|
+
token = auth_header.split(" ")[1]
|
|
24
|
+
if not token:
|
|
25
|
+
return None, ValarResponse(False, status=401)
|
|
26
|
+
try:
|
|
27
|
+
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
|
|
28
|
+
return payload, ValarResponse(False)
|
|
29
|
+
except jwt.ExpiredSignatureError:
|
|
30
|
+
return None, ValarResponse(False, '状态已过期,请重新登录', 'warning', status=401)
|
|
31
|
+
except jwt.InvalidTokenError:
|
|
32
|
+
return None, ValarResponse(False, '错误状态,请重新登录', 'error', status=401)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from django.http import HttpRequest
|
|
5
|
+
from django.utils.deprecation import MiddlewareMixin
|
|
6
|
+
|
|
7
|
+
from src.valar.auth.Authentication import validate
|
|
8
|
+
from src.valar.classes.valar_response import ValarResponse
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ValarMiddleware(MiddlewareMixin):
|
|
12
|
+
@staticmethod
|
|
13
|
+
def process_response(request: HttpRequest, response: ValarResponse):
|
|
14
|
+
auth = request.headers.get("Auth")
|
|
15
|
+
if auth:
|
|
16
|
+
payload, _ = validate(request)
|
|
17
|
+
if not payload:
|
|
18
|
+
return ValarResponse(False, '无效权限', 'error', status=403)
|
|
19
|
+
if type(response) == ValarResponse:
|
|
20
|
+
valar_message, valar_code = response.valar_message, response.valar_code
|
|
21
|
+
data = {
|
|
22
|
+
'payload': json.loads(response.content),
|
|
23
|
+
'message': valar_message,
|
|
24
|
+
'code': valar_code
|
|
25
|
+
}
|
|
26
|
+
response.content = json.dumps(data, ensure_ascii=False).encode("utf-8")
|
|
27
|
+
response["Content-Type"] = "application/json; charset=utf-8"
|
|
28
|
+
return response
|
|
@@ -7,6 +7,7 @@ from channels.layers import get_channel_layer
|
|
|
7
7
|
from django.http import HttpRequest
|
|
8
8
|
import threading
|
|
9
9
|
from .consumer import VALAR_CHANNEL_GROUP
|
|
10
|
+
from ..auth.Authentication import validate
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class Channel:
|
|
@@ -54,7 +55,9 @@ class ValarChannelSender(Sender):
|
|
|
54
55
|
self.__lock__ = threading.Lock()
|
|
55
56
|
self.__interval__ = interval
|
|
56
57
|
if self.__channel__.auth and not self.uid:
|
|
57
|
-
|
|
58
|
+
payload, _ = validate(request)
|
|
59
|
+
if payload is None:
|
|
60
|
+
raise Exception('Unauthorized!')
|
|
58
61
|
|
|
59
62
|
def _run(self):
|
|
60
63
|
while self.__loading__:
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from django.http import JsonResponse
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ValarResponse(JsonResponse):
|
|
5
|
+
def __init__(self, data=True, message='', code='info', status=200):
|
|
6
|
+
self.valar_message = message
|
|
7
|
+
self.valar_code = code
|
|
8
|
+
self.status_code = status
|
|
9
|
+
super(ValarResponse, self).__init__(data, safe=False)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
|
|
3
3
|
from bson import ObjectId
|
|
4
|
+
from django.db.models import QuerySet
|
|
4
5
|
|
|
5
6
|
from ..dao.engine import ValarEngine
|
|
7
|
+
from ..models.core import VModel
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class AbstractField(ABC):
|
|
@@ -12,6 +14,7 @@ class AbstractField(ABC):
|
|
|
12
14
|
label = None
|
|
13
15
|
domain = None
|
|
14
16
|
refer = None
|
|
17
|
+
model_field = None
|
|
15
18
|
|
|
16
19
|
@abstractmethod
|
|
17
20
|
def to_dict(self):
|
|
@@ -51,7 +54,7 @@ class AbstractDao(ABC):
|
|
|
51
54
|
pass
|
|
52
55
|
|
|
53
56
|
@abstractmethod
|
|
54
|
-
def values(self, conditions, props):
|
|
57
|
+
def values(self, conditions, props) -> list:
|
|
55
58
|
pass
|
|
56
59
|
|
|
57
60
|
@abstractmethod
|
|
@@ -59,11 +62,11 @@ class AbstractDao(ABC):
|
|
|
59
62
|
pass
|
|
60
63
|
|
|
61
64
|
@abstractmethod
|
|
62
|
-
def find_one(self, _id):
|
|
65
|
+
def find_one(self, _id) -> VModel:
|
|
63
66
|
pass
|
|
64
67
|
|
|
65
68
|
@abstractmethod
|
|
66
|
-
def find(self, conditions=None, orders=None, size=0, page=1):
|
|
69
|
+
def find(self, conditions=None, orders=None, size=0, page=1) -> (QuerySet, int):
|
|
67
70
|
pass
|
|
68
71
|
|
|
69
72
|
@abstractmethod
|
|
@@ -39,10 +39,19 @@ meta_field_key_defaults = {
|
|
|
39
39
|
]
|
|
40
40
|
),
|
|
41
41
|
},
|
|
42
|
-
'valar.
|
|
42
|
+
'valar.Vala': {
|
|
43
43
|
'simple': ('pick', ['id', 'name', 'text_field', 'boolean_field', 'integer_field', 'float_field']),
|
|
44
44
|
'date': ('pick', ['id', 'name', 'date_field', 'datetime_field', 'time_field']),
|
|
45
45
|
'special': ('pick', ['id', 'name', 'text_field', 'json_field', 'file', 'm2m']),
|
|
46
46
|
'ref': ('pick', ['id', 'name', 'm2o', 'm2m', 'o2o_id']),
|
|
47
47
|
},
|
|
48
|
+
'valar.Account': {
|
|
49
|
+
'auth': ('pick', ['user_id', 'username', 'email', 'is_active', 'is_admin', 'roles']),
|
|
50
|
+
},
|
|
51
|
+
'valar.Role': {
|
|
52
|
+
'auth': ('pick', ['name', 'duty', 'menu', 'account']),
|
|
53
|
+
},
|
|
54
|
+
'valar.Menu': {
|
|
55
|
+
'auth': ('pick', ['icon', 'path', 'name', 'roles', 'is_admin', 'scope']),
|
|
56
|
+
},
|
|
48
57
|
}
|
|
@@ -2,6 +2,8 @@ import pymongo
|
|
|
2
2
|
from django.apps import apps
|
|
3
3
|
from django.conf import settings
|
|
4
4
|
from urllib.parse import urlparse
|
|
5
|
+
|
|
6
|
+
from django.core.mail import EmailMessage
|
|
5
7
|
from minio import Minio
|
|
6
8
|
from pymongo.synchronous.collection import Collection
|
|
7
9
|
|
|
@@ -55,6 +57,13 @@ class ValarEngine(metaclass=SingletonMeta):
|
|
|
55
57
|
else:
|
|
56
58
|
self.minio_engine = None
|
|
57
59
|
|
|
60
|
+
self.from_email = settings.EMAIL_HOST_USER
|
|
61
|
+
|
|
62
|
+
def send_email(self, title, content, email):
|
|
63
|
+
e = EmailMessage(title, content, self.from_email, [email])
|
|
64
|
+
e.content_subtype = 'html'
|
|
65
|
+
e.send()
|
|
66
|
+
|
|
58
67
|
def get_orm_model(self, entity) -> VModel:
|
|
59
68
|
return self.orm_engine[entity]
|
|
60
69
|
|
|
@@ -131,12 +131,10 @@ class OrmField(AbstractField):
|
|
|
131
131
|
|
|
132
132
|
|
|
133
133
|
def column_width(domain, tool):
|
|
134
|
-
if domain in ['BooleanField', '
|
|
135
|
-
return 80
|
|
136
|
-
elif domain in ['DateField', 'DateTimeField', 'TimeField']:
|
|
134
|
+
if domain in ['BooleanField', 'DateField', 'DateTimeField', 'TimeField']:
|
|
137
135
|
return 120
|
|
138
|
-
elif domain in ['ManyToManyRel', '
|
|
139
|
-
return
|
|
140
|
-
elif domain in ['TextField']:
|
|
136
|
+
elif domain in ['ManyToManyRel', 'ManyToManyField', 'ManyToOneRel']:
|
|
137
|
+
return 120
|
|
138
|
+
elif domain in ['TextField', 'FileField', 'JSONField']:
|
|
141
139
|
return 80 if tool == 'rich' else 0
|
|
142
140
|
return 0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import core, frame, meta, auth, test
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from .core import VModel, VTree
|
|
3
|
+
from django.contrib.auth.hashers import check_password
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Role(VModel):
|
|
7
|
+
duty = models.TextField(null=True, verbose_name='职责描述')
|
|
8
|
+
|
|
9
|
+
class Meta:
|
|
10
|
+
verbose_name = '角色'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Account(VModel):
|
|
14
|
+
"""账户核心信息"""
|
|
15
|
+
username = models.CharField(max_length=255, null=True, unique=True, verbose_name='账号')
|
|
16
|
+
password = models.CharField(max_length=255, verbose_name='密码')
|
|
17
|
+
email = models.CharField(max_length=255, null=True, unique=True, verbose_name='邮箱')
|
|
18
|
+
"""权限"""
|
|
19
|
+
is_admin = models.BooleanField(default=False, verbose_name='超级管理员')
|
|
20
|
+
roles = models.ManyToManyField(Role)
|
|
21
|
+
"""密码找回"""
|
|
22
|
+
is_active = models.BooleanField(default=False, verbose_name='激活状态')
|
|
23
|
+
token = models.CharField(max_length=255, null=True, verbose_name='Token')
|
|
24
|
+
|
|
25
|
+
def is_auth(self, password):
|
|
26
|
+
return password == self.token or check_password(password, self.password)
|
|
27
|
+
|
|
28
|
+
class Meta:
|
|
29
|
+
verbose_name = '账户信息'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Menu(VTree):
|
|
33
|
+
scope = models.CharField(max_length=100, null=True, verbose_name='域')
|
|
34
|
+
path = models.CharField(max_length=255, null=True, verbose_name='地址')
|
|
35
|
+
roles = models.ManyToManyField(Role)
|
|
36
|
+
is_admin = models.BooleanField(null=True, default=False, verbose_name='超管权限')
|
|
37
|
+
|
|
38
|
+
class Meta:
|
|
39
|
+
verbose_name = '菜单'
|
|
40
|
+
unique_together = ('scope', 'path')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AbstractUser(VModel):
|
|
44
|
+
account = models.OneToOneField(Account, null=True, on_delete=models.SET_NULL)
|
|
45
|
+
|
|
46
|
+
class Meta:
|
|
47
|
+
abstract = True
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from django.urls import path
|
|
2
|
-
|
|
3
1
|
from .channels.views import handel_channel
|
|
4
|
-
from .views import rest, meta, file
|
|
2
|
+
from .views import rest, meta, file, auth, password
|
|
3
|
+
from django.urls import path
|
|
5
4
|
|
|
6
5
|
urlpatterns = [
|
|
7
6
|
path('socket/<str:handler>', handel_channel),
|
|
@@ -13,7 +12,6 @@ urlpatterns = [
|
|
|
13
12
|
path('<str:db>/<str:entity>/update', rest.update),
|
|
14
13
|
path('<str:db>/<str:entity>/search', rest.search),
|
|
15
14
|
path('<str:db>/<str:entity>/values', rest.values),
|
|
16
|
-
# path('<str:db>/<str:entity>/tree', rest.tree),
|
|
17
15
|
|
|
18
16
|
path('<str:db>/<str:entity>/meta_view', meta.meta_view),
|
|
19
17
|
path('metas', meta.metas),
|
|
@@ -26,4 +24,12 @@ urlpatterns = [
|
|
|
26
24
|
path('<str:db>/<str:entity>/save_file', file.save_file),
|
|
27
25
|
path('<str:db>/<str:entity>/remove_file', file.remove_file),
|
|
28
26
|
|
|
27
|
+
path('sign_in', auth.sign_in),
|
|
28
|
+
path("user_profile", auth.user_profile),
|
|
29
|
+
path("free_menus", auth.free_menus),
|
|
30
|
+
|
|
31
|
+
path("change_password", password.change_password),
|
|
32
|
+
path("retrieve_password", password.retrieve_password),
|
|
33
|
+
path("send_password", password.send_password),
|
|
34
|
+
|
|
29
35
|
]
|
|
File without changes
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
import jwt
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
from django.db.models import OneToOneRel
|
|
7
|
+
from django.contrib.auth.hashers import make_password
|
|
8
|
+
|
|
9
|
+
from ..auth.Authentication import auth_required
|
|
10
|
+
from ..classes.valar_response import ValarResponse
|
|
11
|
+
from ..dao import Dao
|
|
12
|
+
from ..models.auth import Account, AbstractUser, Menu
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def sign_in(request):
|
|
16
|
+
body = json.loads(request.body)
|
|
17
|
+
username = body.get("username")
|
|
18
|
+
email = body.get("email")
|
|
19
|
+
password = body.get("password")
|
|
20
|
+
signin = body.get("signin")
|
|
21
|
+
dao = Dao('valar.Account')
|
|
22
|
+
account = dao.search({"username": username}).first()
|
|
23
|
+
if account is not None:
|
|
24
|
+
if signin:
|
|
25
|
+
if not account.is_auth(password):
|
|
26
|
+
return ValarResponse(False, '密码错误', 'warning', status=400)
|
|
27
|
+
else:
|
|
28
|
+
return ValarResponse(False, f"{username}已被占用", 'warning', status=400)
|
|
29
|
+
else:
|
|
30
|
+
if signin:
|
|
31
|
+
return ValarResponse(False, f"{username}不存在", 'warning', status=400)
|
|
32
|
+
else:
|
|
33
|
+
if username == 'admin' and password != settings.SECRET_KEY:
|
|
34
|
+
return ValarResponse(False, "请输入正确的admin密码", 'warning', status=400)
|
|
35
|
+
else:
|
|
36
|
+
account = dao.save_one({
|
|
37
|
+
"username": username,
|
|
38
|
+
"email": email,
|
|
39
|
+
"password": make_password(password),
|
|
40
|
+
"is_admin": username == 'admin'
|
|
41
|
+
})
|
|
42
|
+
return ValarResponse(jwt.encode({
|
|
43
|
+
"user_id": account.id,
|
|
44
|
+
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30)
|
|
45
|
+
}, settings.SECRET_KEY, algorithm="HS256"))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def free_menus(request):
|
|
49
|
+
body = json.loads(request.body)
|
|
50
|
+
scope = body.get("scope", 'admin')
|
|
51
|
+
menus = Menu.objects.filter(isLeaf=True, scope=scope, is_admin=False, path__isnull=False,
|
|
52
|
+
roles__isnull=True).values('path')
|
|
53
|
+
payload = {
|
|
54
|
+
"permissions": [m['path'] for m in menus]
|
|
55
|
+
}
|
|
56
|
+
token = jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")
|
|
57
|
+
return ValarResponse(token)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@auth_required
|
|
61
|
+
def user_profile(request):
|
|
62
|
+
body = json.loads(request.body)
|
|
63
|
+
scope = body.get("scope", 'admin')
|
|
64
|
+
account_id = request.user_id
|
|
65
|
+
account = Account.objects.filter(id=account_id).first()
|
|
66
|
+
if account is None:
|
|
67
|
+
return ValarResponse(False, f"账户已不存在", 'warning', status=401)
|
|
68
|
+
user_admin = account.is_admin
|
|
69
|
+
user_roles = list(account.roles.values('id', 'name'))
|
|
70
|
+
user_role_keys = [r['id'] for r in user_roles]
|
|
71
|
+
permissions = []
|
|
72
|
+
for m in Menu.objects.filter(isLeaf=True, scope=scope):
|
|
73
|
+
_id = m.path
|
|
74
|
+
if _id is None:
|
|
75
|
+
continue
|
|
76
|
+
if user_admin:
|
|
77
|
+
permissions.append(_id)
|
|
78
|
+
else:
|
|
79
|
+
if not m.is_admin:
|
|
80
|
+
menu_role_keys = [r['id'] for r in m.roles.all().values('id')]
|
|
81
|
+
if len(menu_role_keys):
|
|
82
|
+
if bool(set(user_role_keys) & set(menu_role_keys)):
|
|
83
|
+
permissions.append(_id)
|
|
84
|
+
else:
|
|
85
|
+
permissions.append(_id)
|
|
86
|
+
payload = {
|
|
87
|
+
"username": account.username,
|
|
88
|
+
"roles": user_roles,
|
|
89
|
+
"is_admin": user_admin,
|
|
90
|
+
"email": account.email,
|
|
91
|
+
"is_active": account.is_active,
|
|
92
|
+
"temporary": account.token is not None,
|
|
93
|
+
"permissions": permissions
|
|
94
|
+
}
|
|
95
|
+
print(permissions)
|
|
96
|
+
field = (
|
|
97
|
+
next((field for field in account.get_meta().get_fields()
|
|
98
|
+
if type(field) == OneToOneRel
|
|
99
|
+
and issubclass(field.related_model, AbstractUser)), None))
|
|
100
|
+
if field:
|
|
101
|
+
accessor = field.get_accessor_name()
|
|
102
|
+
model = field.related_model
|
|
103
|
+
entity = f'{model._meta.app_label}.{model.__name__}'
|
|
104
|
+
payload.update({'user_entity': entity, 'user_accessor': accessor})
|
|
105
|
+
if hasattr(account, accessor):
|
|
106
|
+
user = getattr(account, accessor)
|
|
107
|
+
name = getattr(user, 'name')
|
|
108
|
+
payload.update({'name': name})
|
|
109
|
+
token = jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")
|
|
110
|
+
return ValarResponse(token)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import secrets
|
|
3
|
+
from django.contrib.auth.hashers import check_password, make_password
|
|
4
|
+
|
|
5
|
+
from ..auth.Authentication import auth_required
|
|
6
|
+
from ..classes.valar_response import ValarResponse
|
|
7
|
+
|
|
8
|
+
from ..dao.engine import ValarEngine
|
|
9
|
+
from ..models.auth import Account, AbstractUser
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def send_password(request):
|
|
13
|
+
body = json.loads(request.body)
|
|
14
|
+
account_id = body.get('account_id')
|
|
15
|
+
account = Account.objects.filter(id=account_id).first()
|
|
16
|
+
return __send__(account)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def retrieve_password(request):
|
|
20
|
+
includes = json.loads(request.body)
|
|
21
|
+
account = Account.objects.filter(**includes).first()
|
|
22
|
+
account.token = account.token or secrets.token_hex(16)
|
|
23
|
+
account.save()
|
|
24
|
+
if account:
|
|
25
|
+
return __send__(account)
|
|
26
|
+
else:
|
|
27
|
+
return ValarResponse(False, '该邮箱未在系统中注册', code='error')
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@auth_required
|
|
31
|
+
def change_password(request):
|
|
32
|
+
body = json.loads(request.body)
|
|
33
|
+
old_password = body.get('old_password')
|
|
34
|
+
new_password = body.get('new_password')
|
|
35
|
+
account_id = request.user_id
|
|
36
|
+
account = Account.objects.get(id=account_id)
|
|
37
|
+
if not account:
|
|
38
|
+
return ValarResponse(False, '账户不存在', code='error')
|
|
39
|
+
if not account.is_auth(old_password):
|
|
40
|
+
return ValarResponse(False, '当前密码错误', code='error')
|
|
41
|
+
if old_password == new_password:
|
|
42
|
+
return ValarResponse(False, '新密码不能与当前密码一致', code='error')
|
|
43
|
+
account.password = make_password(new_password)
|
|
44
|
+
account.token = None
|
|
45
|
+
account.save()
|
|
46
|
+
return ValarResponse(True)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def __send__(account):
|
|
50
|
+
if not account:
|
|
51
|
+
return ValarResponse(False, '账户不存在', code='error')
|
|
52
|
+
token = account.token
|
|
53
|
+
if not token:
|
|
54
|
+
return ValarResponse(False, '未生成临时密码', code='error')
|
|
55
|
+
email = account.email
|
|
56
|
+
if not email:
|
|
57
|
+
return ValarResponse(False, '该账户未登记邮箱信息', code='error')
|
|
58
|
+
content = f'Your temporary password is {token}, please change it as soon as possible.'
|
|
59
|
+
ValarEngine().send_email('Retrieve Password', content, email)
|
|
60
|
+
return ValarResponse(True, '临时密码发送成功', 'success')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: valar
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: valar for morghulis
|
|
5
5
|
Home-page: https://gitee.com/GRIFFIN120/valar_dev
|
|
6
6
|
Author: LYP
|
|
@@ -16,6 +16,7 @@ Requires-Dist: pandas==2.2.3
|
|
|
16
16
|
Requires-Dist: openpyxl==3.1.5
|
|
17
17
|
Requires-Dist: deepmerge~=2.0
|
|
18
18
|
Requires-Dist: minio==7.2.2
|
|
19
|
+
Requires-Dist: PyJWT~=2.10.1
|
|
19
20
|
Dynamic: author
|
|
20
21
|
Dynamic: author-email
|
|
21
22
|
Dynamic: description
|
|
@@ -9,6 +9,9 @@ src/valar.egg-info/SOURCES.txt
|
|
|
9
9
|
src/valar.egg-info/dependency_links.txt
|
|
10
10
|
src/valar.egg-info/requires.txt
|
|
11
11
|
src/valar.egg-info/top_level.txt
|
|
12
|
+
src/valar/auth/Authentication.py
|
|
13
|
+
src/valar/auth/Middleware.py
|
|
14
|
+
src/valar/auth/__init__.py
|
|
12
15
|
src/valar/channels/__init__.py
|
|
13
16
|
src/valar/channels/consumer.py
|
|
14
17
|
src/valar/channels/counter.py
|
|
@@ -39,12 +42,15 @@ src/valar/dao/defaults/field_values_default.py
|
|
|
39
42
|
src/valar/dao/defaults/view_defaults.py
|
|
40
43
|
src/valar/migrations/__init__.py
|
|
41
44
|
src/valar/models/__init__.py
|
|
45
|
+
src/valar/models/auth.py
|
|
42
46
|
src/valar/models/core.py
|
|
43
47
|
src/valar/models/frame.py
|
|
44
48
|
src/valar/models/meta.py
|
|
45
49
|
src/valar/models/test.py
|
|
46
50
|
src/valar/views/__init__.py
|
|
51
|
+
src/valar/views/auth.py
|
|
47
52
|
src/valar/views/file.py
|
|
48
53
|
src/valar/views/handler.py
|
|
49
54
|
src/valar/views/meta.py
|
|
55
|
+
src/valar/views/password.py
|
|
50
56
|
src/valar/views/rest.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from . import core, frame, meta, test
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|