You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
flyapps/fir_ser/api/utils/app/supersignutils.py

544 lines
25 KiB

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/6
import uuid, xmltodict, os, re, logging, time
from fir_ser.settings import SUPER_SIGN_ROOT, MEDIA_ROOT, SERVER_DOMAIN, MOBILECONFIG_SIGN_SSL
from api.utils.app.iossignapi import AppDeveloperApi, ResignApp, AppDeveloperApiV2
5 years ago
from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo, Apps, APPToDeveloper, \
UDIDsyncDeveloper
5 years ago
from api.utils.app.randomstrings import make_app_uuid, make_from_user_uuid
5 years ago
from api.utils.serializer import get_developer_udided
5 years ago
from api.utils.storage.localApi import LocalStorage
from api.utils.storage.caches import del_cache_response_by_short
from api.utils.utils import file_format_path, delete_app_to_dev_and_file, delete_app_profile_file, \
send_ios_developer_active_status
logger = logging.getLogger(__file__)
5 years ago
def udid_bytes_to_dict(xml_stream):
new_uuid_info = {}
try:
a = xml_stream.find('<plist')
b = xml_stream.find('</plist>')
xml_dict = xmltodict.parse(xml_stream[a:b + 8]) # 解析xml字符串
for i in range(len(xml_dict['plist']['dict']['key'])):
new_uuid_info[xml_dict['plist']['dict']['key'][i].lower()] = xml_dict['plist']['dict']['string'][i]
except Exception as e:
logger.error("udid_xml_stream:%s Exception:%s" % (xml_stream, e))
return None
return new_uuid_info
def make_sign_udid_mobileconfig(udid_url, PayloadOrganization, appname):
if MOBILECONFIG_SIGN_SSL.get("open"):
ssl_key_path = MOBILECONFIG_SIGN_SSL.get("ssl_key_path", None)
ssl_pem_path = MOBILECONFIG_SIGN_SSL.get("ssl_pem_path", None)
if ssl_key_path and ssl_pem_path and os.path.isfile(ssl_key_path) and os.path.isfile(ssl_pem_path):
mobileconfig_tmp_dir = os.path.join(SUPER_SIGN_ROOT, 'tmp', 'mobileconfig')
if not os.path.exists(mobileconfig_tmp_dir):
os.makedirs(mobileconfig_tmp_dir)
mobileconfig_filename = PayloadOrganization + str(uuid.uuid1())
mobilconfig_path = os.path.join(mobileconfig_tmp_dir, mobileconfig_filename)
sign_mobilconfig_path = os.path.join(mobileconfig_tmp_dir, 'sign_' + mobileconfig_filename)
with open(mobilconfig_path, "w") as f:
f.write(make_udid_mobileconfig(udid_url, PayloadOrganization, appname))
status, result = ResignApp.sign_mobileconfig(mobilconfig_path, sign_mobilconfig_path, ssl_pem_path,
ssl_key_path)
if status:
mobileconfig_body = open(sign_mobilconfig_path, 'rb')
else:
logger.error(
"%s %s sign_mobileconfig failed ERROR:%s" % (PayloadOrganization, appname, result.get("err_info")))
return make_udid_mobileconfig(udid_url, PayloadOrganization, appname)
return mobileconfig_body
else:
logger.error("sign_mobileconfig %s or %s is not exists" % (ssl_key_path, ssl_pem_path))
return make_udid_mobileconfig(udid_url, PayloadOrganization, appname)
else:
return make_udid_mobileconfig(udid_url, PayloadOrganization, appname)
def make_udid_mobileconfig(udid_url, PayloadOrganization, appname, PayloadUUID=uuid.uuid1(),
PayloadDescription='该文件仅用来获取设备ID,帮助用户安装授权',
PayloadDisplayName='设备安装授权'):
# <!--参考:https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/ConfigurationProfileExamples/ConfigurationProfileExamples.html-->
5 years ago
mobileconfig = '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<dict>
<key>URL</key>
<string>%s</string>
<key>DeviceAttributes</key>
<array>
<string>SERIAL</string>
<string>UDID</string>
<string>IMEI</string>
<string>ICCID</string>
<string>VERSION</string>
<string>PRODUCT</string>
</array>
</dict>
<key>PayloadOrganization</key>
<string>%s</string>
<key>PayloadDisplayName</key>
<string>%s</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadUUID</key>
<string>%s</string>
<key>PayloadIdentifier</key>
<string>%s.profile-service</string>
<key>PayloadDescription</key>
<string>%s</string>
<key>PayloadType</key>
<string>Profile Service</string>
</dict>
</plist>''' % (udid_url, PayloadOrganization, appname + " -- " + PayloadDisplayName, PayloadUUID, PayloadOrganization,
PayloadDescription)
return mobileconfig
def get_post_udid_url(request, short):
server_domain = get_http_server_doamin(request)
5 years ago
# PATH_INFO = request.META.get('PATH_INFO')
# PATH_INFO_lists = PATH_INFO.strip('/').split('/')
# PATH_INFO_lists[-1] = 'udid'
# PATH_INFO_lists.pop(-2)
# PATH_INFO_lists.append(short)
# PATH_INFO_lists.insert(0, server_domain)
5 years ago
PATH_INFO_lists = [server_domain, "udid", short]
udid_url = "/".join(PATH_INFO_lists)
return udid_url
def get_auth_form_developer(developer_obj):
if developer_obj.email:
auth = {
"username": developer_obj.email,
"password": developer_obj.password,
"certid": developer_obj.certid
}
elif developer_obj.issuer_id:
auth = {
"issuer_id": developer_obj.issuer_id,
"private_key_id": developer_obj.private_key_id,
"p8key": developer_obj.p8key,
"certid": developer_obj.certid
}
else:
auth = {}
return auth
def get_api_obj(auth):
if auth.get("username"):
app_api_obj = AppDeveloperApi(**auth)
elif auth.get("issuer_id"):
app_api_obj = AppDeveloperApiV2(**auth)
else:
app_api_obj = None
return app_api_obj
4 years ago
def get_apple_udid_key(auth):
mpkey = auth.get("username")
if auth.get("issuer_id"):
mpkey = auth.get("issuer_id")
return mpkey
4 years ago
def get_http_server_doamin(request):
server_domain = SERVER_DOMAIN.get('POST_UDID_DOMAIN', None)
if not server_domain or not server_domain.startswith("http"):
HTTP_HOST = request.META.get('HTTP_HOST')
SERVER_PROTOCOL = request.META.get('SERVER_PROTOCOL')
protocol = 'https'
if SERVER_PROTOCOL == 'HTTP/1.1':
protocol = 'http'
server_domain = "%s://%s" % (protocol, HTTP_HOST)
return server_domain
5 years ago
def get_redirect_server_domain(request, user_obj=None, app_domain_name=None):
if user_obj:
if app_domain_name and len(app_domain_name) > 3:
domain_name = app_domain_name
else:
domain_name = user_obj.domain_name
else:
4 years ago
domain_name = None
if domain_name and len(domain_name) > 3:
4 years ago
if domain_name == SERVER_DOMAIN.get("REDIRECT_UDID_DOMAIN").split("//")[1]:
server_domain = SERVER_DOMAIN.get("REDIRECT_UDID_DOMAIN")
else:
server_domain = "%s://%s" % ('http', domain_name) # 第三方域名暂时不支持HTTPS
else:
server_domain = SERVER_DOMAIN.get('REDIRECT_UDID_DOMAIN', None)
if not server_domain or not server_domain.startswith("http"):
HTTP_HOST = request.META.get('HTTP_HOST')
SERVER_PROTOCOL = request.META.get('SERVER_PROTOCOL')
protocol = 'https'
if SERVER_PROTOCOL == 'HTTP/1.1':
protocol = 'http'
server_domain = "%s://%s" % (protocol, HTTP_HOST)
return server_domain
class IosUtils(object):
5 years ago
def __init__(self, udid_info, user_obj, app_obj=None):
5 years ago
self.udid_info = udid_info
self.app_obj = app_obj
5 years ago
self.user_obj = user_obj
self.get_developer_auth()
def get_developer_auth(self):
5 years ago
developer_obj = self.get_developer_user_by_app_udid()
5 years ago
self.developer_obj = developer_obj
self.auth = get_auth_form_developer(developer_obj)
5 years ago
def get_developer_user_by_app_udid(self):
5 years ago
usedeviceobj = APPSuperSignUsedInfo.objects.filter(udid__udid=self.udid_info.get('udid'),
user_id=self.user_obj, developerid__is_actived=True,
developerid__certid__isnull=False).first()
# 只要账户下面存在udid,就可以使用该苹果开发者账户,避免多个开发者账户下面出现同一个udid
if usedeviceobj:
5 years ago
developer_obj = usedeviceobj.developerid
5 years ago
else:
5 years ago
developer_udid_obj = UDIDsyncDeveloper.objects.filter(udid=self.udid_info.get('udid'),
developerid__is_actived=True,
developerid__certid__isnull=False).first()
5 years ago
if developer_udid_obj:
developer_obj = developer_udid_obj.developerid
else:
for developer_obj in AppIOSDeveloperInfo.objects.filter(user_id=self.user_obj,
5 years ago
is_actived=True, certid__isnull=False).order_by(
"created_time"):
5 years ago
usable_number = developer_obj.usable_number
flyapp_used = get_developer_udided(developer_obj)[1]
if flyapp_used < usable_number:
return developer_obj
5 years ago
return None
return developer_obj
5 years ago
def download_profile(self):
bundleId = self.app_obj.bundle_id
5 years ago
app_id = self.app_obj.app_id
device_udid = self.udid_info.get('udid')
device_name = self.udid_info.get('product')
4 years ago
return get_api_obj(self.auth).get_profile(bundleId, app_id, device_udid, device_name,
self.get_profile_full_path(),
self.auth)
5 years ago
def get_profile_full_path(self):
cert_dir_name = make_app_uuid(self.user_obj, get_apple_udid_key(self.auth))
5 years ago
cert_dir_path = os.path.join(SUPER_SIGN_ROOT, cert_dir_name, "profile")
5 years ago
if not os.path.isdir(cert_dir_path):
os.makedirs(cert_dir_path)
5 years ago
provisionName = os.path.join(cert_dir_path, self.app_obj.app_id)
return provisionName + '.mobileprovision'
def resign(self):
5 years ago
if AppUDID.objects.filter(app_id=self.app_obj, udid=self.udid_info.get('udid')).first().is_signed:
5 years ago
apptodev_obj = APPToDeveloper.objects.filter(app_id=self.app_obj).first()
if apptodev_obj:
release_obj = AppReleaseInfo.objects.filter(app_id=self.app_obj, is_master=True).first()
if release_obj.release_id == apptodev_obj.release_file:
logger.info("udid %s exists app_id %s" % (self.udid_info.get('udid'), self.app_obj))
return
logger.info("udid %s not exists app_id %s ,need sign" % (self.udid_info.get('udid'), self.app_obj))
fcount = 3
while fcount > 0:
status, result = self.download_profile()
if not status:
fcount -= 1
logger.warning("udid %s app %s developer %s sign failed %s .try again " % (
self.udid_info.get('udid'), self.app_obj, self.developer_obj, result))
time.sleep(3)
else:
fcount = -1
if fcount != -1:
5 years ago
logger.error("udid %s app %s developer %s sign failed %s" % (
self.udid_info.get('udid'), self.app_obj, self.developer_obj, result))
5 years ago
self.developer_obj.is_actived = False
self.developer_obj.save()
send_ios_developer_active_status(self.developer_obj,
'app %s developer %s sign failed %s. disable this developer' % (
4 years ago
self.app_obj, self.developer_obj, result))
self.get_developer_auth()
self.resign()
5 years ago
return
5 years ago
file_format_path_name = file_format_path(self.user_obj, self.auth)
5 years ago
my_local_key = file_format_path_name + ".key"
app_dev_pem = file_format_path_name + ".pem"
ResignAppObj = ResignApp(my_local_key, app_dev_pem)
random_file_name = make_from_user_uuid(self.user_obj)
5 years ago
release_obj = AppReleaseInfo.objects.filter(app_id=self.app_obj, is_master=True).first()
org_file = os.path.join(MEDIA_ROOT, release_obj.release_id + ".ipa")
new_file = os.path.join(MEDIA_ROOT, random_file_name + ".ipa")
5 years ago
ResignAppObj.sign(self.get_profile_full_path(), org_file, new_file)
5 years ago
newdata = {
"is_signed": True,
5 years ago
"binary_file": random_file_name
}
5 years ago
# 更新已经完成签名状态,设备消耗记录,和开发者已消耗数量
5 years ago
AppUDID.objects.filter(app_id=self.app_obj, udid=self.udid_info.get('udid')).update(**newdata)
5 years ago
appsupersign_obj = APPSuperSignUsedInfo.objects.filter(udid__udid=self.udid_info.get('udid'),
developerid=self.developer_obj)
5 years ago
if appsupersign_obj.count() == 0:
developer_obj = self.developer_obj
5 years ago
developer_obj.use_number = developer_obj.use_number + 1
logger.info("developer %s use_number+1 now %s udid %s app_id %s" % (
developer_obj, developer_obj.use_number, self.udid_info.get('udid'), self.app_obj))
developer_obj.save()
5 years ago
if not appsupersign_obj.filter(app_id=self.app_obj, user_id=self.user_obj).first():
5 years ago
APPSuperSignUsedInfo.objects.create(app_id=self.app_obj, user_id=self.user_obj,
developerid=self.developer_obj,
udid=AppUDID.objects.filter(app_id=self.app_obj,
udid=self.udid_info.get('udid')).first())
del_cache_response_by_short(self.app_obj.app_id, udid=self.udid_info.get('udid'))
5 years ago
app_udid_obj = UDIDsyncDeveloper.objects.filter(developerid=self.developer_obj, udid=self.udid_info.get('udid'))
if not app_udid_obj:
5 years ago
device = AppUDID.objects.filter(app_id=self.app_obj, udid=self.udid_info.get('udid')).values("serial",
'product',
'udid',
'version').first()
5 years ago
UDIDsyncDeveloper.objects.create(developerid=self.developer_obj, **device)
5 years ago
# 创建
apptodev_obj = APPToDeveloper.objects.filter(developerid=self.developer_obj, app_id=self.app_obj).first()
if apptodev_obj:
storage = LocalStorage("localhost", False)
5 years ago
storage.del_file(apptodev_obj.binary_file + ".ipa")
apptodev_obj.binary_file = random_file_name
apptodev_obj.release_file = release_obj.release_id
apptodev_obj.save()
else:
5 years ago
APPToDeveloper.objects.create(developerid=self.developer_obj, app_id=self.app_obj,
binary_file=random_file_name, release_file=release_obj.release_id)
5 years ago
@staticmethod
5 years ago
def disable_udid(udid_obj, app_id):
5 years ago
usedeviceobj = APPSuperSignUsedInfo.objects.filter(udid=udid_obj, app_id_id=app_id)
5 years ago
if usedeviceobj:
developer_obj = usedeviceobj.first().developerid
auth = get_auth_form_developer(developer_obj)
# 需要判断该设备在同一个账户下 的多个应用,若存在,则不操作
5 years ago
udid_lists = list(APPSuperSignUsedInfo.objects.values_list("udid__udid").filter(developerid=developer_obj))
if udid_lists.count((udid_obj.udid,)) == 1:
app_api_obj = get_api_obj(auth)
5 years ago
app_api_obj.set_device_status("disable", udid_obj.udid)
if developer_obj.use_number > 0:
5 years ago
developer_obj.use_number = developer_obj.use_number - 1
developer_obj.save()
usedeviceobj.delete()
# 通过开发者id判断 app_id 是否多个,否则删除profile 文件
if APPSuperSignUsedInfo.objects.filter(developerid=developer_obj, app_id_id=app_id).count() == 0:
app_api_obj = get_api_obj(auth)
app_obj = Apps.objects.filter(pk=app_id).first()
5 years ago
app_api_obj.del_profile(app_obj.bundle_id, app_obj.app_id)
app_api_obj2 = get_api_obj(auth)
5 years ago
app_api_obj2.del_app(app_obj.bundle_id, app_obj.app_id)
5 years ago
delete_app_to_dev_and_file(developer_obj, app_id)
5 years ago
delete_app_profile_file(developer_obj, app_obj)
@staticmethod
5 years ago
def clean_udid_by_app_obj(app_obj, developer_obj):
auth = get_auth_form_developer(developer_obj)
# 需要判断该设备在同一个账户下 的多个应用,若存在,则不操作
udid_lists = list(APPSuperSignUsedInfo.objects.values_list("udid__udid").filter(developerid=developer_obj))
for SuperSignUsed_obj in APPSuperSignUsedInfo.objects.filter(app_id=app_obj, developerid=developer_obj):
udid_obj = SuperSignUsed_obj.udid
if udid_lists.count((udid_obj.udid,)) == 1:
app_api_obj = get_api_obj(auth)
app_api_obj.set_device_status("disable", udid_obj.udid)
5 years ago
if developer_obj.use_number > 0:
developer_obj.use_number = developer_obj.use_number - 1
developer_obj.save()
udid_obj.delete()
5 years ago
SuperSignUsed_obj.delete()
@staticmethod
5 years ago
def clean_app_by_user_obj(app_obj, user_obj):
'''
该APP为超级签删除app的时候需要清理一下开发者账户里面的profile bundleid
:param app_obj:
:param user_obj:
:return:
'''
5 years ago
SuperSign_obj_lists = list(
set(APPSuperSignUsedInfo.objects.values_list("developerid").filter(user_id=user_obj, app_id=app_obj)))
for SuperSign_obj in SuperSign_obj_lists:
5 years ago
developer_obj = AppIOSDeveloperInfo.objects.filter(pk=SuperSign_obj[0]).first()
if developer_obj:
5 years ago
IosUtils.clean_app_by_developer_obj(app_obj, developer_obj)
5 years ago
delete_app_to_dev_and_file(developer_obj, app_obj.id)
5 years ago
IosUtils.clean_udid_by_app_obj(app_obj, developer_obj)
5 years ago
delete_app_profile_file(developer_obj, app_obj)
5 years ago
@staticmethod
5 years ago
def clean_app_by_developer_obj(app_obj, developer_obj):
auth = get_auth_form_developer(developer_obj)
app_api_obj = get_api_obj(auth)
app_api_obj.del_profile(app_obj.bundle_id, app_obj.app_id)
app_api_obj2 = get_api_obj(auth)
5 years ago
app_api_obj2.del_app(app_obj.bundle_id, app_obj.app_id)
@staticmethod
5 years ago
def clean_developer(developer_obj, user_obj):
'''
根据消耗记录 删除该苹果账户下所有信息
:param developer_obj:
:return:
'''
for APPToDeveloper_obj in APPToDeveloper.objects.filter(developerid=developer_obj):
5 years ago
app_obj = APPToDeveloper_obj.app_id
IosUtils.clean_app_by_developer_obj(app_obj, developer_obj)
5 years ago
delete_app_to_dev_and_file(developer_obj, app_obj.id)
IosUtils.clean_udid_by_app_obj(app_obj, developer_obj)
full_path = file_format_path(user_obj, get_auth_form_developer(developer_obj))
5 years ago
try:
for root, dirs, files in os.walk(full_path, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
os.rmdir(full_path)
except Exception as e:
logger.error("clean_developer developer_obj:%s user_obj:%s delete file failed Exception:%s" % (
developer_obj, user_obj, e))
5 years ago
@staticmethod
5 years ago
def active_developer(developer_obj, user_obj):
'''
激活开发者账户
:param developer_obj:
:param code:
:return:
'''
auth = get_auth_form_developer(developer_obj)
app_api_obj = get_api_obj(auth)
5 years ago
status, result = app_api_obj.active(user_obj)
5 years ago
if status:
5 years ago
developer_obj.is_actived = True
5 years ago
developer_obj.save()
5 years ago
return status, result
@staticmethod
5 years ago
def create_developer_cert(developer_obj, user_obj):
auth = get_auth_form_developer(developer_obj)
app_api_obj = get_api_obj(auth)
5 years ago
status, result = app_api_obj.create_cert(user_obj)
5 years ago
if status:
if auth.get("issuer_id"):
cert_id = result.id
4 years ago
AppIOSDeveloperInfo.objects.filter(user_id=user_obj, issuer_id=auth.get("issuer_id")).update(
is_actived=True,
certid=cert_id)
else:
file_format_path_name = file_format_path(user_obj, auth)
cert_info = None
try:
with open(file_format_path_name + '.info', "r") as f:
cert_info = f.read()
except Exception as e:
4 years ago
logger.error(
"create_developer_cert developer_obj:%s user_obj:%s delete file failed Exception:%s" % (
developer_obj, user_obj, e))
if cert_info:
cert_id = re.findall(r'.*\n\tid=(.*),.*', cert_info)[0].replace('"', '')
4 years ago
AppIOSDeveloperInfo.objects.filter(user_id=user_obj, email=auth.get("username")).update(
is_actived=True,
certid=cert_id)
5 years ago
return status, result
5 years ago
@staticmethod
5 years ago
def get_device_from_developer(developer_obj, user_obj):
auth = get_auth_form_developer(developer_obj)
app_api_obj = get_api_obj(auth)
5 years ago
status, result = app_api_obj.get_device(user_obj)
5 years ago
if status:
if auth.get("issuer_id"):
UDIDsyncDeveloper.objects.filter(developerid=developer_obj, platform=1).delete()
for device_obj in result:
5 years ago
device = {
"serial": device_obj.id,
"product": device_obj.name,
"udid": device_obj.udid,
"version": device_obj.model,
"platform": 1
5 years ago
}
5 years ago
app_udid_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj, udid=device.get("udid"))
5 years ago
if app_udid_obj:
pass
else:
5 years ago
UDIDsyncDeveloper.objects.create(developerid=developer_obj, **device)
else:
file_format_path_name = file_format_path(user_obj, auth)
devices_info = ""
try:
with open(file_format_path_name + ".devices.info", "r") as f:
devices_info = f.read().replace("\n\t", "").replace("[", "").replace("]", "")
except Exception as e:
logger.error(
"get_device_from_developer developer_obj:%s user_obj:%s delete file failed Exception:%s" % (
developer_obj, user_obj, e))
UDIDsyncDeveloper.objects.filter(developerid=developer_obj, platform=1).delete()
for devicestr in devices_info.split(">"):
4 years ago
formatdevice = re.findall(r'.*Device id="(.*)",.*name="(.*)",.*udid="(.*?)",.*model=(.*),.*',
devicestr)
if formatdevice:
device = {
"serial": formatdevice[0][0],
"product": formatdevice[0][1],
"udid": formatdevice[0][2],
"version": formatdevice[0][3],
"platform": 1
}
4 years ago
app_udid_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj,
udid=device.get("udid"))
if app_udid_obj:
pass
else:
UDIDsyncDeveloper.objects.create(developerid=developer_obj, **device)
5 years ago
return status, result