diff --git a/fir_client/src/components/FirDownload.vue b/fir_client/src/components/FirDownload.vue index 1ff404e..a6010c0 100644 --- a/fir_client/src/components/FirDownload.vue +++ b/fir_client/src/components/FirDownload.vue @@ -417,7 +417,8 @@ export default { mobileprovision: '', ad_info: {ad_pic: '', ad_uri: ''}, task_spend_time: 0, - task_msg: '执行中' + task_msg: '执行中', + err_password: false } }, beforeDestroy() { @@ -565,6 +566,7 @@ export default { } else { this.$message.success('签名完成,请点击下载安装'); this.wrong = false; + this.binary_download(); } } else { if (data.code === 1002) { @@ -600,6 +602,22 @@ export default { window.location.href = this.mobileprovision; }, check_msg() { + if (this.currentappinfo.need_password) { + if (this.currentappinfo.issupersign && !this.$route.query.udid) { + this.currentappinfo.need_password = false + } + if (this.$route.query.password && !this.err_password) { + this.currentappinfo.need_password = false + this.password = this.$route.query.password; + this.download() + } + } else { + if (this.$route.query.udid && this.$route.query.task_token) { + this.download() + } + } + }, + download() { if (this.$route.query.udid && this.$route.query.task_token) { this.wrong = true; this.msg = '签名处理中,请耐心等待'; @@ -612,18 +630,32 @@ export default { this.show_err_msg(data.result); } else { this.wrong = false; + this.currentappinfo.need_password = false + this.binary_download() } + } else if (data.code === 1006) { + this.msg = data.msg; + this.show_err_msg(data.msg); + this.wrong = false; + this.err_password = true + this.currentappinfo.need_password = true } else { this.show_err_msg(data.msg); + // eslint-disable-next-line no-unused-vars + this.timer = setTimeout(data => { + window.location.href = location.href.replace(location.search, ''); + }, 3000); } }, { methods: 'POST', short: this.$route.params.short, - data: {"task_token": this.$route.query.task_token} + data: {"task_token": this.$route.query.task_token, 'password': this.password} }) + } else { + this.binary_download() } }, - download() { + binary_download() { if (this.currentappinfo.app_id) { this.isdownload = true; getdownloadurl(res => { @@ -693,6 +725,8 @@ export default { window.location.reload(); } this.password = ''; + this.err_password = true; + this.currentappinfo.need_password = true; } }, { 'data': { diff --git a/fir_client/src/components/ShortDownload.vue b/fir_client/src/components/ShortDownload.vue index 5e480d6..0362c8c 100644 --- a/fir_client/src/components/ShortDownload.vue +++ b/fir_client/src/components/ShortDownload.vue @@ -406,7 +406,8 @@ export default { mobileprovision: '', ad_info: {ad_pic: '', ad_uri: ''}, task_spend_time: 0, - task_msg: '执行中' + task_msg: '执行中', + err_password: false } }, beforeDestroy() { @@ -530,6 +531,7 @@ export default { this.show_err_msg(data.msg); } else { this.wrong = false; + this.binary_download(); } } else { if (data.code === 1002) { @@ -565,6 +567,22 @@ export default { window.location.href = this.mobileprovision; }, check_msg() { + if (this.currentappinfo.need_password) { + if (this.currentappinfo.issupersign && !this.$route.query.udid) { + this.currentappinfo.need_password = false + } + if (this.$route.query.password && !this.err_password) { + this.currentappinfo.need_password = false + this.password = this.$route.query.password; + this.download() + } + } else { + if (this.$route.query.udid && this.$route.query.task_token) { + this.download() + } + } + }, + download() { if (this.$route.query.udid && this.$route.query.task_token) { this.wrong = true; this.msg = '签名处理中,请耐心等待'; @@ -577,18 +595,32 @@ export default { this.show_err_msg(data.result); } else { this.wrong = false; + this.currentappinfo.need_password = false + this.binary_download() } + } else if (data.code === 1006) { + this.msg = data.msg; + this.show_err_msg(data.msg); + this.wrong = false; + this.err_password = true; + this.currentappinfo.need_password = true; } else { this.show_err_msg(data.msg); + // eslint-disable-next-line no-unused-vars + this.timer = setTimeout(data => { + window.location.href = location.href.replace(location.search, ''); + }, 3000); } }, { methods: 'POST', short: this.$route.params.short, - data: {"task_token": this.$route.query.task_token} + data: {"task_token": this.$route.query.task_token, 'password': this.password} }) + } else { + this.binary_download() } }, - download() { + binary_download() { if (this.currentappinfo.app_id) { this.isdownload = true; getdownloadurl(res => { @@ -652,6 +684,8 @@ export default { window.location.reload(); } this.password = ''; + this.err_password = true; + this.currentappinfo.need_password = true; } }, { 'data': { diff --git a/fir_client/src/components/apps/FirAppInfossecurity.vue b/fir_client/src/components/apps/FirAppInfossecurity.vue index ddce1a0..2cacfbe 100644 --- a/fir_client/src/components/apps/FirAppInfossecurity.vue +++ b/fir_client/src/components/apps/FirAppInfossecurity.vue @@ -15,7 +15,7 @@ :visible.sync="download_password_sure" center title="应用下载授权码设置" - width="800px"> + width="900px"> - + 0表示不限使用次数 - + + +
+ 开启绑定设备 + 关闭绑定设备 +
+ + +
+ 绑定设备之后,二次自动进行设备授权下载应用 +
取消 @@ -56,8 +73,8 @@ @@ -123,13 +140,29 @@ align="center" label="最大可用次数" prop="max_limit_count" - width="140"> + width="130"> - +
+ + + + + +
= 4: AppDownloadToken.objects.update_or_create(app_id=app_obj, token=token, - defaults={"max_limit_count": token_max_used_number}) + defaults={"max_limit_count": token_max_used_number, + "bind_status": bind_status}) else: if isinstance(token_length, int) and 4 <= token_length <= 32: pass @@ -401,8 +409,8 @@ class AppDownloadTokenView(APIView): exist_token=copy.deepcopy(exist_token)) bulk_list = [] for d_token in list(set(make_token_list) - set(exist_token))[:token_number]: - bulk_list.append( - AppDownloadToken(token=d_token, max_limit_count=token_max_used_number, app_id=app_obj)) + bulk_list.append(AppDownloadToken(token=d_token, max_limit_count=token_max_used_number, app_id=app_obj, + bind_status=bind_status)) AppDownloadToken.objects.bulk_create(bulk_list) return Response(res.dict) @@ -413,14 +421,15 @@ class AppDownloadTokenView(APIView): act = request.data.get('act') if token is not None: AppDownloadToken.objects.filter(app_id__user_id=request.user, - app_id__app_id=app_id, token=token).update(used_count=0) + app_id__app_id=app_id, token=token).update(used_count=0, bind_udid=None) if act: if act == 'all': AppDownloadToken.objects.filter(app_id__user_id=request.user, app_id__app_id=app_id).delete() elif act == 'invalid': AppDownloadToken.objects.filter(app_id__user_id=request.user, app_id__app_id=app_id, - used_count__gte=F('max_limit_count'), max_limit_count__gt=0).delete() + used_count__gte=F('max_limit_count'), + max_limit_count__gt=0, bind_udid__isnull=False).delete() elif act == 'some': tokens = request.data.get('tokens') if tokens and isinstance(tokens, list): diff --git a/fir_ser/common/utils/download.py b/fir_ser/common/utils/download.py index b8df3ff..4ed310e 100644 --- a/fir_ser/common/utils/download.py +++ b/fir_ser/common/utils/download.py @@ -34,11 +34,11 @@ def get_download_url_by_cache(app_obj, filename, limit, is_download=True, key='' else: local_storage = LocalStorage(**Config.IOS_PMFILE_DOWNLOAD_DOMAIN) download_url_type = 'plist' - if not udid: + if udid: + return run_xsign_app_download_url(app_obj, udid, download_url_type, limit) + else: if app_obj.get('issupersign', None): download_url_type = 'mobileconifg' - else: - return run_xsign_app_download_url(app_obj, udid, download_url_type, limit) supersign = Config.DEFAULT_MOBILEPROVISION.get("supersign") mobileconifg = "" @@ -63,17 +63,7 @@ def get_download_url_by_cache(app_obj, filename, limit, is_download=True, key='' return local_storage.get_download_url(filename.split(".")[0] + "." + download_url_type, limit), mobileconifg -def get_app_instance_by_cache(app_id, password, is_download, limit, udid): - if udid: - app_info = Apps.objects.filter(app_id=app_id).values("pk", 'user_id', 'type', 'need_password', 'issupersign', - 'user_id__certification__status').first() - if app_info: - app_info['d_count'] = get_app_d_count_by_app_id(app_id) - need_password = app_info.get("need_password") - if need_password and not is_download: - if not check_app_access_token(app_id, password): - return False, '下载授权码有误' - return True, app_info +def get_app_instance_by_cache(app_id, limit): app_instance_cache = AppInstanceCache(app_id) app_obj_cache = app_instance_cache.get_storage_cache() if not app_obj_cache: @@ -85,11 +75,6 @@ def get_app_instance_by_cache(app_id, password, is_download, limit, udid): app_instance_cache.set_storage_cache(app_obj_cache, limit) if not app_obj_cache: return False, '应用不存在' - need_password = app_obj_cache.get("need_password") - if need_password and not is_download: - if not check_app_access_token(app_id, password): - return False, '下载授权码有误' - return True, app_obj_cache @@ -114,8 +99,16 @@ def set_app_download_by_cache(app_id, limit=900): return download_times + 1 +def check_app_download_token(need_password, is_download, app_id, password, only_check=False, udid=None): + if need_password: + if is_download or check_app_access_token(app_id, password, only_check, udid): + return True + else: + return True + + def get_app_download_url(request, res, app_id, short, password, release_id, is_download, udid): - status, app_obj = get_app_instance_by_cache(app_id, password, is_download, 900, udid) + status, app_obj = get_app_instance_by_cache(app_id, 900) if status: if app_obj.get("type") == 0: app_type = '.apk' @@ -127,6 +120,12 @@ def get_app_download_url(request, res, app_id, short, password, release_id, is_d res.data = {"download_url": download_url, "extra_url": extra_url} if download_url != "" and "mobileconifg" not in download_url: + + if not check_app_download_token(app_obj.get("need_password"), is_download, app_id, password): + res.code = 1006 + res.msg = '下载授权码有误' + return res + ip = get_real_ip_address(request) msg = f"remote ip {ip} short {short} download_url {download_url} app_obj {app_obj}" logger.info(msg) diff --git a/fir_ser/xsign/views/receiveudids.py b/fir_ser/xsign/views/receiveudids.py index 6521bfa..efd1c27 100644 --- a/fir_ser/xsign/views/receiveudids.py +++ b/fir_ser/xsign/views/receiveudids.py @@ -13,7 +13,7 @@ from django.views import View from rest_framework.response import Response from rest_framework.views import APIView -from api.models import Apps +from api.models import Apps, AppDownloadToken from api.utils.modelutils import get_redirect_server_domain, add_remote_info_from_request, \ get_app_download_uri from api.utils.response import BaseResponse @@ -22,6 +22,7 @@ from common.cache.storage import TaskStateCache from common.core.sysconfig import Config from common.core.throttle import ReceiveUdidThrottle1, ReceiveUdidThrottle2, VisitShortThrottle, InstallShortThrottle from common.utils.caches import check_app_permission +from common.utils.download import check_app_download_token from common.utils.pending import get_pending_result from common.utils.token import verify_token, make_token from fir_ser.celery import app @@ -73,6 +74,10 @@ class IosUDIDView(APIView): } encrypt_data = AesBaseCrypt().get_encrypt_uid(json.dumps(data)) msg = "&task_token=%s" % quote(encrypt_data, safe='/', encoding=None, errors=None) + token_obj = AppDownloadToken.objects.filter(app_id__app_id=app_id, + bind_udid=format_udid_info.get('udid')).first() + if token_obj: + msg = f"{msg}&password={token_obj.token}" else: return HttpResponsePermanentRedirect(f"{server_domain}/{short}") else: @@ -140,6 +145,7 @@ class TaskView(APIView): def post(self, request, short): res = BaseResponse() task_token = request.data.get('task_token', None) + password = request.data.get('password', None) client_ip = get_real_ip_address(request) if task_token: data = json.loads(AesBaseCrypt().get_decrypt_uid(task_token)) @@ -155,21 +161,26 @@ class TaskView(APIView): res.msg = '错误,数据异常,请重试' res.code = 1003 - if not verify_token(data.get('r_token', ''), app_obj.app_id, True): - res.msg = '非法,数据异常,请重试' + format_udid_info = data.get('format_udid_info') + udid = format_udid_info.get('udid') + + if not verify_token(data.get('r_token', ''), app_obj.app_id, False): + res.msg = '授权过期,请重试' res.code = 1004 + if not check_app_download_token(app_obj.need_password, False, app_obj.app_id, password, False, udid): + res.code = 1006 + res.msg = '下载授权码有误' + if res.code != 1000: return Response(res.dict) - format_udid_info = data.get('format_udid_info') - c_task = run_sign_task.apply_async((format_udid_info, short, client_ip)) add_remote_info_from_request(request, f'{app_obj}-{format_udid_info}') task_id = c_task.id logger.info(f"sign app {app_obj} task_id:{task_id}") try: - result = c_task.get(propagate=False, timeout=3) + result = c_task.get(propagate=False, timeout=1) except TimeoutError: logger.error(f"get task task_id:{task_id} result timeout") result = '' diff --git a/fir_ser/xsign/views/supersign.py b/fir_ser/xsign/views/supersign.py index 0699752..6a8a5a0 100644 --- a/fir_ser/xsign/views/supersign.py +++ b/fir_ser/xsign/views/supersign.py @@ -118,7 +118,7 @@ class DeveloperView(APIView): if devicestatus: run_queryset = run_queryset.filter(udidsyncdeveloper__status=devicestatus) - for developer_s_obj in run_queryset: + for developer_s_obj in run_queryset.distinct(): pools.submit(run_task, developer_s_obj) pools.shutdown() @@ -419,7 +419,7 @@ class SuperSignUsedView(APIView): app_to_dev_obj = APPToDeveloper.objects.filter(app_id=app_obj, developerid__issuer_id=developer_id).first() - res = get_app_download_url(request, res, app_obj.app_id, app_obj.short, app_obj.password, + res = get_app_download_url(request, res, app_obj.app_id, app_obj.short, None, app_to_dev_obj.binary_file, True, device_udid) return Response(res.dict)