增加域名访问权限控制,用户绑定的域名只能允许本用户应用访问

publicsignpoll
youngS 3 years ago
parent f6918011ca
commit 3325814c03
  1. 2
      fir_client/src/components/FirBase.vue
  2. 35
      fir_client/src/components/base/BindDomain.vue
  3. 2
      fir_ser/api/utils/app/supersignutils.py
  4. 13
      fir_ser/api/utils/baseutils.py
  5. 41
      fir_ser/api/utils/storage/caches.py
  6. 2
      fir_ser/api/utils/utils.py
  7. 120
      fir_ser/api/views/domain.py
  8. 43
      fir_ser/api/views/download.py

@ -20,7 +20,7 @@
<div slot="title">
<span :underline="false"
class="domian-tip-bar">应用分发请绑定您自己的域名平台分发域名可能因不可违因素更换将导致您的应用无法访问</span>
<el-button size="medium" @click="bind_domain_sure=true">立即绑定</el-button>
<el-button size="medium" @click="bind_domain_type=1,bind_domain_sure=true">立即绑定</el-button>
</div>
</el-alert>
</div>

@ -36,12 +36,26 @@
align="center"
label="主机记录"
>
<template slot-scope="scope">
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.host" :underline="false" v-clipboard:copy="scope.row.host"
v-clipboard:success="copy_success">{{ scope.row.host }}
</el-link>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
prop="dns"
align="center"
label="记录值"
width="300">
<template slot-scope="scope">
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="scope.row.dns" :underline="false" v-clipboard:copy="scope.row.dns"
v-clipboard:success="copy_success">{{ scope.row.dns }}
</el-link>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<el-alert title="请在域名DNS配置成功后,点击“下一步”按钮"
@ -123,7 +137,9 @@
<el-button @click="last"
:disabled="bind_status|| active===1 ">上一步
</el-button>
<el-button @click="next">下一步</el-button>
<el-button v-if="force_bind" @click="next(1)">强制绑定</el-button>
<el-button v-else @click="next(0)">下一步</el-button>
</div>
</div>
@ -156,6 +172,7 @@
bind_domain_sure: true,
domain_name: '',
domain_tData: [{'type': 'CNAME', 'host': 'xxx', 'dns': 'demo.xxx.cn'}],
force_bind:false,
}
},
mounted() {
@ -165,13 +182,18 @@
this.bind_domain_sure = false;
},
methods: {
copy_success() {
this.$message.success('复制剪切板成功');
},
check_cname() {
domainFun(data => {
if (data.code === 1000) {
if (this.active++ > 2) this.active = 3;
this.bind_status = true;
if (!this.app_id) {
this.$store.dispatch("dodomainshow", false);
if (this.domain_type === 1) {
this.$store.dispatch("dodomainshow", false);
}
}
} else {
if (data.code === 1004) {
@ -234,7 +256,7 @@
}
this.domain_tData[0].dns = cname_domain;
},
next() {
next(force_bind) {
if (this.active === 1) {
domainFun(data => {
if (data.code === 1000) {
@ -243,11 +265,14 @@
if (this.active++ > 2) this.active = 3;
}
} else {
this.$message.error("绑定失败 " + data.msg)
this.$message.error("绑定失败 " + data.msg);
if(data.code === 1011){
this.force_bind=true
}
}
}, {
methods: 'POST',
data: {domain_name: this.domain_name, app_id: this.app_id, domain_type: this.domain_type}
data: {domain_name: this.domain_name, app_id: this.app_id, domain_type: this.domain_type,force_bind:force_bind}
})
} else if (this.active === 2) {
this.check_cname()

@ -4,7 +4,7 @@
# author: liuyu
# date: 2020/3/6
import uuid, xmltodict, os, re, logging, time
import uuid, xmltodict, os, logging, time
import zipfile
from api.utils.response import BaseResponse

@ -229,3 +229,16 @@ def get_filename_form_file(filename):
f_type = '.ipa'
return f"{app_obj.name}-{app_release_obj.app_version}-{app_obj.short}{f_type}"
return filename
def check_app_domain_name_access(app_obj, access_domain_name, extra_domain=None):
if app_obj and access_domain_name:
domain_list = []
if extra_domain:
domain_list.append(extra_domain)
app_domain_name = get_app_domain_name(app_obj)
if app_domain_name: domain_list.append(app_domain_name)
user_domain_name = get_user_domain_name(app_obj.user_id)
if user_domain_name: domain_list.append(user_domain_name)
if access_domain_name in domain_list:
return True

@ -13,7 +13,7 @@ from django.utils import timezone
from fir_ser.settings import CACHE_KEY_TEMPLATE, SERVER_DOMAIN, SYNC_CACHE_TO_DATABASE, DEFAULT_MOBILEPROVISION, \
USER_FREE_DOWNLOAD_TIMES, AUTH_USER_FREE_DOWNLOAD_TIMES
from api.utils.storage.storage import Storage, LocalStorage
from api.utils.baseutils import get_app_d_count_by_app_id, get_app_domain_name, check_app_password
from api.utils.baseutils import get_app_d_count_by_app_id, get_app_domain_name, check_app_password, get_user_domain_name
import logging
from django.db.models import F
@ -146,12 +146,16 @@ def del_cache_response_by_short(app_id, udid=''):
del_cache_response_by_short_util(combo_dict.get("short"), combo_dict.get("app_id"), udid)
def del_cache_response_by_short_util(short, app_id, udid):
logger.info(f"del_cache_response_by_short short:{short} app_id:{app_id} udid:{udid}")
def del_short_cache(short):
key = "_".join([CACHE_KEY_TEMPLATE.get("download_short_key"), short, '*'])
for app_download_key in cache.iter_keys(key):
cache.delete(app_download_key)
def del_cache_response_by_short_util(short, app_id, udid):
logger.info(f"del_cache_response_by_short short:{short} app_id:{app_id} udid:{udid}")
del_short_cache(short)
cache.delete("_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"), app_id]))
key = 'ShortDownloadView'.lower()
@ -274,16 +278,27 @@ def send_msg_over_limit(act, email):
return limit_cache_util(act, auth_code_key, SYNC_CACHE_TO_DATABASE.get("try_send_msg_over_limit_times"))
def set_default_app_wx_easy(user_obj, only_clean_cache=False):
app_obj_lists = Apps.objects.filter(user_id=user_obj)
for app_obj in app_obj_lists:
if only_clean_cache:
del_cache_response_by_short(app_obj.app_id)
else:
if not get_app_domain_name(app_obj):
app_obj.wxeasytype = True
app_obj.save(update_fields=['wxeasytype'])
del_cache_response_by_short(app_obj.app_id)
def reset_short_response_cache(user_obj, app_obj=None):
if app_obj is None:
app_obj_short_list = [x[0] for x in Apps.objects.filter(user_id=user_obj).values_list('short').all() if x]
else:
app_obj_short_list = [app_obj.short]
for short in app_obj_short_list:
del_short_cache(short)
def reset_app_wx_easy_type(user_obj, app_obj=None):
if get_user_domain_name(user_obj):
return reset_short_response_cache(user_obj, app_obj)
if app_obj is None:
app_obj_list = Apps.objects.filter(user_id=user_obj).all()
else:
app_obj_list = [app_obj]
for app_obj in app_obj_list:
if not get_app_domain_name(app_obj):
app_obj.wxeasytype = True
app_obj.save(update_fields=['wxeasytype'])
del_short_cache(app_obj.short)
def enable_user_download_times_flag(user_id):

@ -7,7 +7,7 @@ import os, datetime, random
import binascii
from fir_ser.settings import SERVER_DOMAIN, CAPTCHA_LENGTH, MEDIA_ROOT, CACHE_KEY_TEMPLATE
from api.models import APPSuperSignUsedInfo, APPToDeveloper, \
UDIDsyncDeveloper, UserInfo, AppReleaseInfo, AppScreenShot, Token, DeveloperDevicesID, Apps
UDIDsyncDeveloper, UserInfo, AppReleaseInfo, AppScreenShot, Token, DeveloperDevicesID
from api.utils.storage.caches import get_app_d_count_by_app_id
from api.utils.storage.localApi import LocalStorage
from api.utils.storage.storage import Storage

@ -14,7 +14,7 @@ from rest_framework.response import Response
from api.models import UserDomainInfo, Apps
import logging
from api.utils.storage.caches import set_default_app_wx_easy, del_cache_response_by_short
from api.utils.storage.caches import del_cache_response_by_short, reset_app_wx_easy_type, reset_short_response_cache
logger = logging.getLogger(__name__)
@ -31,6 +31,32 @@ def get_domain_filter(request):
return filter_dict
def remove_domain_wx_easy(app_obj, user_obj):
if app_obj and not get_user_domain_name(user_obj):
app_obj.wxeasytype = True
app_obj.save(update_fields=['wxeasytype'])
del_cache_response_by_short(app_obj.app_id)
def add_new_domain_info(res, request, domain_name, domain_type):
min_domain_cname_info_obj = get_min_default_domain_cname_obj(False)
if min_domain_cname_info_obj:
res.data = {'cname_domain': min_domain_cname_info_obj.domain_record}
data_dict = {
'user_id': request.user,
'cname_id': min_domain_cname_info_obj,
'domain_name': domain_name,
'app_id': None,
'domain_type': domain_type
}
app_id = request.data.get("app_id", None)
if app_id:
app_obj = Apps.objects.filter(app_id=app_id).first()
if app_obj:
data_dict['app_id'] = app_obj
UserDomainInfo.objects.create(**data_dict)
class DomainCnameView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
@ -49,6 +75,7 @@ class DomainCnameView(APIView):
res = BaseResponse()
domain_name = request.data.get("domain_name", None)
domain_type = request.data.get('domain_type', 1)
force_bind = request.data.get('force_bind', 0)
if domain_type not in [x[0] for x in list(UserDomainInfo.domain_type_choices)]:
res.code = 1001
res.msg = "绑定失败"
@ -56,33 +83,37 @@ class DomainCnameView(APIView):
if domain_name:
domain_name = domain_name.strip(" ")
if domain_name and len(domain_name) > 3 and is_valid_domain(domain_name):
if UserDomainInfo.objects.filter(domain_name=domain_name, is_enable=True,
domain_type=domain_type).count() != 0:
res.code = 1001
res.msg = "该域名已经被绑定,请更换其他域名"
filter_dict = get_domain_filter(request)
if not force_bind:
user_domain_obj = UserDomainInfo.objects.filter(domain_name=domain_name, is_enable=True).first()
if user_domain_obj:
res.code = 1011
if user_domain_obj.user_id.uid == request.user.uid:
app_obj = user_domain_obj.app_id
if app_obj:
res.msg = f"该域名已经被应用 {app_obj.name} 绑定"
elif user_domain_obj.domain_type == 0:
res.msg = f"该域名已经被 应用下载码绑定"
else:
res.msg = f"该域名已经被 预览下载页绑定"
else:
res.msg = "该域名已经被其他用户绑定"
res.msg += ",请更换要绑定的域名,或者进行强制绑定域名"
else:
user_domain_obj = UserDomainInfo.objects.filter(**get_domain_filter(request),
domain_name=domain_name).first()
if user_domain_obj:
res.data = {'cname_domain': user_domain_obj.cname_id.domain_record}
else:
UserDomainInfo.objects.filter(**filter_dict, is_enable=False).delete()
add_new_domain_info(res, request, domain_name, domain_type)
else:
user_domain_obj = UserDomainInfo.objects.filter(**get_domain_filter(request),
user_domain_obj = UserDomainInfo.objects.filter(**filter_dict,
domain_name=domain_name).first()
if user_domain_obj:
res.data = {'cname_domain': user_domain_obj.cname_id.domain_record}
else:
UserDomainInfo.objects.filter(**get_domain_filter(request), is_enable=False).delete()
min_domain_cname_info_obj = get_min_default_domain_cname_obj(False)
if min_domain_cname_info_obj:
res.data = {'cname_domain': min_domain_cname_info_obj.domain_record}
data_dict = {
'user_id': request.user,
'cname_id': min_domain_cname_info_obj,
'domain_name': domain_name,
'app_id': None,
'domain_type': domain_type
}
app_id = request.data.get("app_id", None)
if app_id:
app_obj = Apps.objects.filter(app_id=app_id).first()
if app_obj:
data_dict['app_id'] = app_obj
UserDomainInfo.objects.create(**data_dict)
add_new_domain_info(res, request, domain_name, domain_type)
else:
res.code = 1002
res.msg = "该域名校验失败,请检查"
@ -94,19 +125,26 @@ class DomainCnameView(APIView):
if user_domain_obj:
cname = get_cname_from_domain(user_domain_obj.domain_name)
if cname == user_domain_obj.cname_id.domain_record + '.':
user_domain_obj.is_enable = True
user_domain_obj.save(update_fields=["is_enable"])
UserDomainInfo.objects.filter(domain_name=user_domain_obj.domain_name, is_enable=False,
domain_type=user_domain_obj.domain_type).delete()
app_id = request.data.get("app_id", None)
if app_id:
app_obj = Apps.objects.filter(app_id=app_id).first()
if app_obj:
app_obj.wxeasytype = False
app_obj.save(update_fields=['wxeasytype'])
del_cache_response_by_short(app_obj.app_id)
user_domain_obj_list = UserDomainInfo.objects.filter(domain_name=user_domain_obj.domain_name,
is_enable=True).all()
if len(user_domain_obj_list) < 2:
if len(user_domain_obj_list) == 1:
user_obj = user_domain_obj_list.first().user_id
app_obj = user_domain_obj_list.first().app_id
user_domain_obj_list.delete()
reset_app_wx_easy_type(user_obj, app_obj)
user_domain_obj.is_enable = True
user_domain_obj.save(update_fields=["is_enable"])
app_id = request.data.get("app_id", None)
app_obj = None
if app_id:
app_obj = Apps.objects.filter(app_id=app_id).first()
reset_app_wx_easy_type(request.user, app_obj)
else:
set_default_app_wx_easy(request.user, True)
res.code = 1002
res.msg = "该域名查询校验失败,请检查"
else:
res.code = 1003
res.msg = "系统未检出到您的CNAME记录"
@ -117,14 +155,12 @@ class DomainCnameView(APIView):
def delete(sele, request):
res = BaseResponse()
if UserDomainInfo.objects.filter(**get_domain_filter(request)).delete()[0]:
user_domain_obj = UserDomainInfo.objects.filter(**get_domain_filter(request)).first()
if user_domain_obj:
app_id = request.query_params.get("app_id", None)
app_obj = None
if app_id:
app_obj = Apps.objects.filter(app_id=app_id).first()
if app_obj and not get_user_domain_name(request.user):
app_obj.wxeasytype = True
app_obj.save(update_fields=['wxeasytype'])
del_cache_response_by_short(app_obj.app_id)
else:
set_default_app_wx_easy(request.user)
reset_app_wx_easy_type(request.user, app_obj)
user_domain_obj.delete()
return Response(res.dict)

@ -3,6 +3,8 @@
# project: 3月
# author: liuyu
# date: 2020/3/6
from urllib.parse import urlsplit
from rest_framework.views import APIView
from api.utils.TokenManager import verify_token
@ -21,7 +23,8 @@ from api.utils.serializer import AppsShortSerializer
from api.models import Apps, AppReleaseInfo, APPToDeveloper, APPSuperSignUsedInfo
from django.http import FileResponse
import logging
from api.utils.baseutils import get_profile_full_path, get_app_domain_name, get_filename_form_file
from api.utils.baseutils import get_profile_full_path, get_app_domain_name, get_filename_form_file, \
check_app_domain_name_access
from api.utils.throttle import VisitShortThrottle, InstallShortThrottle, InstallThrottle1, InstallThrottle2
logger = logging.getLogger(__name__)
@ -146,19 +149,24 @@ class ShortDownloadView(APIView):
res = check_app_permission(app_obj, res)
if res.code != 1000:
return Response(res.dict)
origin_domain_name = request.META.get('HTTP_ORIGIN', 'xx').split('//')[-1]
domain_name = get_redirect_server_domain(request, app_obj.user_id, get_app_domain_name(app_obj))
if not check_app_domain_name_access(app_obj, origin_domain_name, domain_name.split('//')[-1]):
res.code = 1004
res.msg = "访问域名不合法"
else:
if udid:
if not app_obj.issupersign:
res.code = 1002
res.msg = "参数有误"
return Response(res.dict)
del_cache_response_by_short(app_obj.app_id, udid=udid)
if udid:
if not app_obj.issupersign:
res.code = 1002
res.msg = "参数有误"
return Response(res.dict)
del_cache_response_by_short(app_obj.app_id, udid=udid)
app_serializer = AppsShortSerializer(app_obj, context={"key": "ShortDownloadView", "release_id": release_id,
"storage": Storage(app_obj.user_id)})
res.data = app_serializer.data
res.udid = udid
res.domain_name = get_redirect_server_domain(request, app_obj.user_id, get_app_domain_name(app_obj))
app_serializer = AppsShortSerializer(app_obj, context={"key": "ShortDownloadView", "release_id": release_id,
"storage": Storage(app_obj.user_id)})
res.data = app_serializer.data
res.udid = udid
res.domain_name = domain_name
return Response(res.dict)
# key的设置
@ -167,13 +175,18 @@ class ShortDownloadView(APIView):
release_id = request.query_params.get("release_id", '')
udid = request.query_params.get("udid", None)
time = request.query_params.get("time", None)
origin_domain_name = request.META.get('HTTP_ORIGIN', 'xx').split('//')[-1]
if not origin_domain_name:
origin_domain_name = 'default.site'
if udid and time:
udid = time
if not udid:
udid = ""
logging.info(f"get or make cache_response short:{kwargs.get('short', '')} release_id:{release_id} udid:{udid}")
logging.info(
f"get or make cache_response short:{kwargs.get('short', '')} origin_domain_name:{origin_domain_name} release_id:{release_id} udid:{udid}")
return "_".join(
[settings.CACHE_KEY_TEMPLATE.get("download_short_key"), kwargs.get("short", ''), release_id, udid])
[settings.CACHE_KEY_TEMPLATE.get("download_short_key"), kwargs.get("short", ''), origin_domain_name,
release_id, udid])
class InstallView(APIView):

Loading…
Cancel
Save