diff --git a/fir_client/src/components/user/FirSuperSignBase.vue b/fir_client/src/components/user/FirSuperSignBase.vue
index 08c56d1..33ab6fb 100644
--- a/fir_client/src/components/user/FirSuperSignBase.vue
+++ b/fir_client/src/components/user/FirSuperSignBase.vue
@@ -106,7 +106,7 @@
导出证书
+ 证书检测
+
导入p12证书
@@ -325,6 +328,68 @@
+
+ 期望的证书ID:{{ editdeveloperinfo.certid }}
+ {{ certinfo_err }}
+
+
+
+
+ 证书ID: {{ scope.row.certid }}
+ 证书序列号: {{ scope.row.serial_number }}
+ 证书平台: {{ scope.row.platform }}
+ 证书名称: {{ scope.row.name }}
+ 证书显示名字: {{ scope.row.display_name }}
+
+ {{ scope.row.certid }}
+
+
+
+
+
+
+
+
+
+
+ 属于
+
+ 其他
+
+
+
+
+
+
+
+
+
+ 关闭
+
+
@@ -519,6 +584,7 @@
align="center"
label="设备消耗"
prop="use_number"
+ sortable
width="60">
@@ -535,6 +601,7 @@
align="center"
label="应用签名"
prop="app_used_count"
+ sortable
width="60">
@@ -555,7 +622,8 @@
是
@@ -598,6 +667,7 @@
align="center"
label="证书到期时间"
prop="cert_expire_time"
+ sortable
width="100">
开发者ID: {{ get_developer_uid(scope.row.issuer_id) }}
开发者备注: {{ scope.row.developer_description }}
开发者状态: {{ scope.row.developer_status }}
+ 安装异常???
+
+ 尝试重新签包
+
+
{{ get_developer_uid(scope.row.issuer_id) }}
@@ -1315,6 +1390,9 @@ export default {
components: {AppleDeveloperBindApp},
data() {
return {
+ certinfovisible: false,
+ certinfo: [],
+ certinfo_err: '',
dialogShowDeviceBillInfo: false,
currentudid: '',
balance_info: {all_balance: 0, used_balance: 0},
@@ -1692,6 +1770,18 @@ export default {
return '#F50346';
}
},
+ resign(row_info) {
+ let other_uid = row_info.other_uid
+ let uid = ''
+ if (other_uid && other_uid.uid) {
+ uid = other_uid.uid
+ }
+ this.iosdevicesudidFun('POST', {
+ id: row_info.id,
+ aid: row_info.app_id,
+ uid: uid
+ }, null)
+ },
udidDeleteFun(scope, disabled) {
this.$confirm('此操作会禁用该苹果开发者账户下面的该设备,可能会导致超级签包的闪退, 是否继续?', '警告', {
confirmButtonText: '确定',
@@ -1768,6 +1858,12 @@ export default {
developercert(data => {
}, {methods: 'FILE', data: {issuer_id: this.editdeveloperinfo.issuer_id}})
},
+ checkcert() {
+ this.iosdeveloperFun({
+ "methods": "PUT",
+ "data": {"issuer_id": this.editdeveloperinfo.issuer_id, "act": 'checkcert'}
+ });
+ },
isorenewcert(act) {
this.$confirm('此操作将永久删除该发布证书, 建议先导出证书。是否继续删除?', '提示', {
confirmButtonText: '确定',
@@ -1943,7 +2039,7 @@ export default {
} else {
this.loading = true
}
- if (params.methods === 'PUT') {
+ if (params.methods === 'PUT' && params.data.act !== 'checkcert') {
params.data.size = this.pagination.pagesize
params.data.page = this.pagination.currentPage
}
@@ -1973,6 +2069,13 @@ export default {
this.setdeveloperstatusVisible = false
this.change_developer_status = ''
}
+ if (params.data.act === 'checkcert') {
+ this.certinfo = data.data.cert_info;
+ this.certinfo_err = data.data.return_info;
+ this.loadingfun.close();
+ this.certinfovisible = true
+ return
+ }
if (this.dialogaddDeveloperVisible) {
this.canceledit();
this.$message.success("操作成功");
@@ -2071,13 +2174,15 @@ export default {
}
iosdevicesudid(data => {
if (data.code === 1000) {
- if (action !== "DELETE") {
+ if (action === "GET") {
this.app_udid_lists = data.data;
this.pagination.total = data.count;
- } else {
+ } else if (action === "DELETE") {
if (scope) {
this.app_udid_lists = removeAaary(this.app_udid_lists, scope.row)
}
+ } else if (action === 'POST') {
+ this.$message.success("操作成功");
}
} else {
this.$message.error("操作失败了 " + data.msg);
diff --git a/fir_ser/api/views/login_wx.py b/fir_ser/api/views/login_wx.py
index b0fd027..a68aab8 100644
--- a/fir_ser/api/views/login_wx.py
+++ b/fir_ser/api/views/login_wx.py
@@ -1,5 +1,6 @@
import logging
+from django.http import HttpResponse
from rest_framework.response import Response
from rest_framework.views import APIView
@@ -134,13 +135,13 @@ class WeChatWebLoginView(APIView):
'openid': wx_user_info.get('openid'),
'nickname': wx_user_info.get('nickname'),
'sex': wx_user_info.get('sex'),
- 'subscribe_time': wx_user_info.get('subscribe_time', 0),
+ # 'subscribe_time': wx_user_info.get('subscribe_time', 0),
'head_img_url': wx_user_info.get('headimgurl', ''),
'address': f"{wx_user_info.get('country')}-{wx_user_info.get('province')}-{wx_user_info.get('city')}",
- 'subscribe': wx_user_info.get('subscribe', 0),
+ # 'subscribe': wx_user_info.get('subscribe', 0),
}
logger.info(f'{wx_user_info}')
ThirdWeChatUserInfo.objects.filter(openid=wx_user_info.get('openid')).update(**wx_user_info)
- return Response('更新成功')
+ return HttpResponse('更新成功
')
ret.data = wx_login_obj.make_auth_uri()
return Response(ret.dict)
diff --git a/fir_ser/common/base/magic.py b/fir_ser/common/base/magic.py
index d17b0a1..76f386b 100644
--- a/fir_ser/common/base/magic.py
+++ b/fir_ser/common/base/magic.py
@@ -64,3 +64,11 @@ def call_function_try_attempts(try_attempts=3, sleep_time=2, failed_callback=Non
return wrapper
return decorator
+
+
+def magic_wrapper(func, *args, **kwargs):
+ @wraps(func)
+ def wrapper():
+ return func(*args, **kwargs)
+
+ return wrapper
diff --git a/fir_ser/common/libs/apple/appleapiv3.py b/fir_ser/common/libs/apple/appleapiv3.py
index 806457c..2e53c53 100644
--- a/fir_ser/common/libs/apple/appleapiv3.py
+++ b/fir_ser/common/libs/apple/appleapiv3.py
@@ -870,8 +870,8 @@ class AppStoreConnectApi(DevicesAPI, BundleIDsAPI, BundleIDsCapabilityAPI, Profi
return self.__profile_store(req)
@call_function_try_attempts(try_attempts=2)
- def get_all_certificates(self):
- req = self.list_certificate()
+ def get_all_certificates(self, query_parameters=None):
+ req = self.list_certificate(query_parameters)
return self.__certificates_store(req)
@call_function_try_attempts()
diff --git a/fir_ser/common/libs/mp/wechat.py b/fir_ser/common/libs/mp/wechat.py
index f87527e..7dd2d10 100644
--- a/fir_ser/common/libs/mp/wechat.py
+++ b/fir_ser/common/libs/mp/wechat.py
@@ -27,6 +27,7 @@ def format_req_json(j_data, func, *args, **kwargs):
if not status:
return result
return func(*args, **kwargs)[1]
+ logger.info(f'j_data:{j_data}')
return j_data
@@ -174,6 +175,8 @@ class WxMsgCrypt(WxMsgCryptBase):
class WxTemplateMsg(object):
def send_msg(self, to_user, template_id, content):
+ if not Config.THIRDLOGINCONF.get('active'):
+ return False, f'weixin status is disabled'
msg_uri = f'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={get_wx_access_token_cache()}'
data = {
"touser": to_user,
@@ -184,7 +187,7 @@ class WxTemplateMsg(object):
}
req = requests.post(msg_uri, json=data)
if req.status_code == 200:
- return True, format_req_json(req.json(), get_userinfo_from_openid, to_user)
+ return True, format_req_json(req.json(), self.send_msg, to_user, template_id, content)
logger.error(f"send msg from openid failed {req.status_code} {req.text}")
return False, req.text
diff --git a/fir_ser/xsign/tasks.py b/fir_ser/xsign/tasks.py
index cc23972..3856e0c 100644
--- a/fir_ser/xsign/tasks.py
+++ b/fir_ser/xsign/tasks.py
@@ -48,12 +48,18 @@ def run_sign_task(format_udid_info, short, client_ip):
return msg
-def run_resign_task(app_id, need_download_profile=True, force=True):
+def run_resign_task(app_id, need_download_profile=True, force=True, developers_filter=None):
+ if developers_filter is None:
+ developers_filter = []
app_obj = Apps.objects.filter(pk=app_id).first()
if app_obj.issupersign and app_obj.user_id.supersign_active:
+ developer_app_id_queryset = DeveloperAppID.objects.filter(app_id=app_obj)
+ if developers_filter:
+ developer_app_id_queryset = developer_app_id_queryset.filter(developerid__in=developers_filter)
+
with cache.lock("%s_%s" % ('task_resign', app_obj.app_id), timeout=60 * 60):
task_list = []
- for developer_app_id_obj in DeveloperAppID.objects.filter(app_id=app_obj).all():
+ for developer_app_id_obj in developer_app_id_queryset.all():
c_task = run_resign_task_do.apply_async((app_id, developer_app_id_obj.developerid.pk,
developer_app_id_obj.aid, need_download_profile, force))
task_list.append(c_task)
diff --git a/fir_ser/xsign/utils/iossignapi.py b/fir_ser/xsign/utils/iossignapi.py
index 4339ce6..791f9b4 100644
--- a/fir_ser/xsign/utils/iossignapi.py
+++ b/fir_ser/xsign/utils/iossignapi.py
@@ -254,7 +254,7 @@ class AppDeveloperApiV2(object):
attr = object.__getattribute__(self, name)
if hasattr(attr, '__call__'):
def func(*args, **kwargs):
- if attr.__name__ in ['active', 'get_device', '__result_format', '__callback_run']:
+ if attr.__name__ in ['get_developer_cert_info', 'get_device', '__result_format', '__callback_run']:
return attr(*args, **kwargs)
else:
if AppIOSDeveloperInfo.objects.filter(pk=self.developer_pk,
@@ -313,14 +313,14 @@ class AppDeveloperApiV2(object):
logger.warning(
f'issuer_id:{self.issuer_id} {callback_info}-{failed_call_prefix} is running')
- def active(self):
+ def get_developer_cert_info(self, query_parameters=None):
"""
:return: 结果为空列表,或者是 object 或者是 [object,object] 其他为 false
"""
result = {}
try:
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
- certificates = apple_obj.get_all_certificates()
+ certificates = apple_obj.get_all_certificates(query_parameters)
return self.__result_format(certificates, Certificates)
except Exception as e:
logger.error(f"issuer_id:{self.issuer_id} ios developer active Failed Exception:{e}")
@@ -392,16 +392,18 @@ class AppDeveloperApiV2(object):
cer = load_certificate(FILETYPE_PEM, open(app_dev_pem, 'rb').read())
not_after = datetime.datetime.strptime(cer.get_notAfter().decode('utf-8'), "%Y%m%d%H%M%SZ")
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
- certificates = apple_obj.get_all_certificates()
+ certificates = apple_obj.get_all_certificates({'filter[certificateType]': 'IOS_DISTRIBUTION'})
status, result = self.__result_format(certificates, Certificates)
if status:
for cert_obj in result:
f_date = format_apple_date(cert_obj.expirationDate)
logger.info(
- f"issuer_id:{self.issuer_id} {cert_obj.id}-{not_after.timestamp()} - {f_date.timestamp()} ")
- if not_after.timestamp() == f_date.timestamp():
+ f"issuer_id:{self.issuer_id} {cert_obj.id}-{not_after.timestamp()} - {f_date.timestamp()} - {cer.get_serial_number()} - {cert_obj.serialNumber} ")
+ # if not_after.timestamp() == f_date.timestamp(): # 比较证书的序列号来判断是否为同一个证书
+ if cer.get_serial_number() == int(cert_obj.serialNumber, 16):
return True, cert_obj
- raise Exception(str(certificates))
+ result = {}
+ raise Exception(str('证书不匹配,请更换其他开发证书重新导入'))
except Exception as e:
logger.error(f"issuer_id:{self.issuer_id} ios developer cert {app_dev_pem} auto get Failed Exception:{e}")
result['return_info'] = check_error_call_back(str(e), self.developer_pk)
diff --git a/fir_ser/xsign/utils/supersignutils.py b/fir_ser/xsign/utils/supersignutils.py
index c545260..4bc2b24 100644
--- a/fir_ser/xsign/utils/supersignutils.py
+++ b/fir_ser/xsign/utils/supersignutils.py
@@ -9,7 +9,6 @@ import os
import time
import uuid
import zipfile
-from functools import wraps
import xmltodict
from django.core.cache import cache
@@ -20,7 +19,7 @@ from api.utils.response import BaseResponse
from api.utils.utils import delete_local_files, download_files_form_oss
from common.base.baseutils import file_format_path, delete_app_profile_file, get_profile_full_path, format_apple_date, \
get_format_time, make_app_uuid, make_from_user_uuid
-from common.base.magic import run_function_by_locker, call_function_try_attempts
+from common.base.magic import run_function_by_locker, call_function_try_attempts, magic_wrapper
from common.cache.state import CleanErrorBundleIdSignDataState
from common.core.sysconfig import Config
from common.utils.caches import del_cache_response_by_short, send_msg_over_limit, check_app_permission, \
@@ -214,14 +213,6 @@ def get_apple_udid_key(auth):
return m_key
-def err_callback(func, *args, **kwargs):
- @wraps(func)
- def wrapper():
- return func(*args, **kwargs)
-
- return wrapper
-
-
def disable_developer_and_send_email(app_obj, developer_obj):
logger.error(f"app {app_obj} developer {developer_obj} sign failed. so disabled")
developer_obj.status = 5
@@ -579,7 +570,7 @@ class IosUtils(object):
register_failed_callback = [
{
- 'func_list': [err_callback(IosUtils.get_device_from_developer, developer_obj)],
+ 'func_list': [magic_wrapper(IosUtils.get_device_from_developer, developer_obj)],
'err_match_msg': [
"There are no current ios devices",
"Your development team has reached the maximum number of registered iPhone devices"
@@ -588,14 +579,14 @@ class IosUtils(object):
]
set_failed_callback = [
{
- 'func_list': [err_callback(IosUtils.get_device_from_developer, developer_obj)],
+ 'func_list': [magic_wrapper(IosUtils.get_device_from_developer, developer_obj)],
'err_match_msg': [
"There are no current ios devices",
"Device obj is None"
]
},
{
- 'func_list': [err_callback(IosUtils.check_device_status, developer_obj)],
+ 'func_list': [magic_wrapper(IosUtils.check_device_status, developer_obj)],
'err_match_msg': [
"ENTITY_ERROR.ATTRIBUTE.INVALID.DUPLICATE",
]
@@ -672,7 +663,7 @@ class IosUtils(object):
failed_callback = []
failed_callback.extend([
{
- 'func_list': [err_callback(IosUtils.clean_super_sign_things_by_app_obj, app_obj, developer_obj)],
+ 'func_list': [magic_wrapper(IosUtils.clean_super_sign_things_by_app_obj, app_obj, developer_obj)],
'err_match_msg': ["There is no App ID with ID"]
}
])
@@ -719,15 +710,15 @@ class IosUtils(object):
failed_callback.extend([
{
- 'func_list': [err_callback(IosUtils.clean_super_sign_things_by_app_obj, app_obj, developer_obj)],
+ 'func_list': [magic_wrapper(IosUtils.clean_super_sign_things_by_app_obj, app_obj, developer_obj)],
'err_match_msg': ["There is no App ID with ID"]
},
{
- 'func_list': [err_callback(IosUtils.active_developer, developer_obj, True)],
+ 'func_list': [magic_wrapper(IosUtils.active_developer, developer_obj)],
'err_match_msg': ["There are no current certificates on this team matching the provided certificate"]
},
{
- 'func_list': [err_callback(IosUtils.check_device_status, developer_obj)],
+ 'func_list': [magic_wrapper(IosUtils.check_device_status, developer_obj)],
'err_match_msg': ["There are no current ios devices on this team matching the provided device IDs"]
}
])
@@ -1052,6 +1043,43 @@ class IosUtils(object):
except Exception as e:
return False, {'err_info': str(e)}
+ @staticmethod
+ def get_developer_cert_info(developer_obj):
+ """
+ 获取开发者证书信息
+ :param developer_obj:
+ :return:
+ """
+ app_api_obj = get_api_obj(developer_obj)
+ # status, result = app_api_obj.get_developer_cert_info({'filter[certificateType]': 'IOS_DISTRIBUTION'})
+ status, result = app_api_obj.get_developer_cert_info()
+ if status:
+ cert_info = []
+ cert_is_exists = True
+ for cert_obj in result:
+ c_info = {
+ 'certid': cert_obj.id,
+ 'serial_number': cert_obj.serialNumber,
+ 'name': cert_obj.name,
+ 'display_name': cert_obj.displayName,
+ 'platform': cert_obj.platform,
+ 'cert_expire_time': format_apple_date(cert_obj.expirationDate),
+ 'c_type': cert_obj.certificateType,
+ 'exist': False}
+ if cert_obj.id == developer_obj.certid and cert_is_exists:
+ developer_obj.cert_expire_time = format_apple_date(cert_obj.expirationDate)
+ cert_is_exists = False
+ c_info['exist'] = True
+ cert_info.append(c_info)
+ cert_info.sort(key=lambda x: x['exist'] == False)
+ if developer_obj.certid and len(developer_obj.certid) > 3 and cert_is_exists and len(result) > 0:
+ # 多次判断数据库证书id和苹果开发id不一致,可认为被用户删掉,需要执行清理开发者操作
+ return status, {'return_info': '证书异常,苹果开发者证书与平台记录ID不一致', 'cert_info': cert_info}
+ if developer_obj.certid and len(developer_obj.certid) > 3 and len(result) == 0:
+ return status, {'return_info': '证书异常,苹果开发者证书为空,疑似证书在苹果开发平台被删除', 'cert_info': cert_info}
+ return status, {'cert_info': cert_info}
+ return status, result
+
@staticmethod
def active_developer(developer_obj, auto_clean=True, loop_count=1):
"""
@@ -1062,7 +1090,7 @@ class IosUtils(object):
:return:
"""
app_api_obj = get_api_obj(developer_obj)
- status, result = app_api_obj.active()
+ status, result = app_api_obj.get_developer_cert_info({'filter[certificateType]': 'IOS_DISTRIBUTION'})
if status:
cert_is_exists = True
for cert_obj in result:
@@ -1091,6 +1119,8 @@ class IosUtils(object):
f"{developer_obj} loop check developer cert.auto_clean:{auto_clean} loop_count:{loop_count}")
return IosUtils.active_developer(developer_obj, auto_clean, loop_count)
+ if developer_obj.certid and len(developer_obj.certid) > 3 and len(result) == 0:
+ return False, {'return_info': '证书异常,苹果开发者证书为空,疑似证书在苹果开发平台被删除'}
developer_obj.save(update_fields=['certid', 'cert_expire_time', 'status'])
return status, result
diff --git a/fir_ser/xsign/views/supersign.py b/fir_ser/xsign/views/supersign.py
index bd97d50..0697533 100644
--- a/fir_ser/xsign/views/supersign.py
+++ b/fir_ser/xsign/views/supersign.py
@@ -23,7 +23,7 @@ from common.utils.download import get_app_download_url
from xsign.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID, IosDeveloperPublicPoolBill, \
UDIDsyncDeveloper, AppleDeveloperToAppUse, DeveloperAppID, APPToDeveloper, DeveloperDevicesID, \
IosDeveloperBill
-from xsign.tasks import run_resign_task_do
+from xsign.tasks import run_resign_task_do, run_resign_task
from xsign.utils.modelutils import get_user_public_used_sign_num, get_user_public_sign_num, check_uid_has_relevant, \
get_developer_devices
from xsign.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer, \
@@ -145,6 +145,15 @@ class DeveloperView(APIView):
res.code = 1008
res.msg = result.get("return_info")
return Response(res.dict)
+ elif act == "checkcert":
+ if developer_obj.certid:
+ status, result = IosUtils.get_developer_cert_info(developer_obj)
+ if status:
+ res.data = result
+ else:
+ res.code = 1008
+ res.msg = result.get("return_info")
+ return Response(res.dict)
elif act in ["renewcert", "cleancert"]:
if developer_obj.certid:
# clean developer somethings. remove profile and revoke cert
@@ -408,6 +417,24 @@ class AppUDIDUsedView(APIView):
res.msg = '数据异常,删除失败'
return Response(res.dict)
+ def post(self, request):
+ res = BaseResponse()
+ pk = request.data.get("id", None)
+ other_uid = request.data.get("uid", None)
+ app_id = request.data.get("aid", None)
+ app_udid_obj = AppUDID.objects.filter(pk=pk, app_id__user_id=request.user)
+ if not app_udid_obj and check_uid_has_relevant(request.user.uid, other_uid):
+ app_udid_obj = AppUDID.objects.filter(pk=pk, app_id__user_id__uid=other_uid)
+ if app_udid_obj:
+ super_sign_used_obj = APPSuperSignUsedInfo.objects.filter(udid=app_udid_obj.first()).first()
+ if super_sign_used_obj:
+ run_resign_task(app_id, need_download_profile=True, force=True,
+ developers_filter=[super_sign_used_obj.developerid])
+ else:
+ res.code = 10003
+ res.msg = '数据异常,重签名失败'
+ return Response(res.dict)
+
class DeveloperDeviceView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
@@ -491,12 +518,12 @@ class SuperSignCertView(APIView):
status, result = resign_app_obj.make_cert_from_p12(request.data.get('cert_pwd', ''),
request.data.get('cert_content', None))
if status:
- status, result = IosUtils.auto_get_cert_id_by_p12(developer_obj, request.user)
+ status, _ = IosUtils.auto_get_cert_id_by_p12(developer_obj, request.user)
if status:
resign_app_obj.write_cert()
else:
res.code = 1003
- res.msg = '证书未在开发者账户找到,请检查推送证书是否属于该开发者'
+ res.msg = '证书未在开发者账户找到,请检查证书是否属于该开发者'
else:
res.code = 1002
res.msg = str(result['err_info'])