超级签 支持自定义bundleid 和 签名类型

pull/6/head
youngS 4 years ago
parent a263d3cf46
commit 348c6a16f5
  1. 18
      fir_client/src/components/FirAppInfosBase.vue
  2. 58
      fir_client/src/components/FirAppInfossecurity.vue
  3. 205
      fir_client/src/components/FirAppInfossupersign.vue
  4. 7
      fir_client/src/router/index.js
  5. 22
      fir_ser/api/migrations/0018_auto_20210308_1352.py
  6. 22
      fir_ser/api/migrations/0019_auto_20210308_1355.py
  7. 18
      fir_ser/api/migrations/0020_auto_20210308_1453.py
  8. 4
      fir_ser/api/models.py
  9. 77
      fir_ser/api/utils/app/iossignapi.py
  10. 176
      fir_ser/api/utils/app/supersignutils.py
  11. 15
      fir_ser/api/utils/apple/appleapiv3.py
  12. 8
      fir_ser/api/utils/serializer.py
  13. 4
      fir_ser/api/utils/utils.py
  14. 33
      fir_ser/api/views/apps.py
  15. 2
      fir_ser/api/views/receiveudids.py
  16. 12
      fir_ser/api/views/uploads.py
  17. 2
      fir_ser/tests/postudid.py

@ -12,7 +12,7 @@
<span><i class="el-icon-cloudy"></i><b class="ng-binding">{{ appinfos.count_hits }}</b></span> <span><i class="el-icon-cloudy"></i><b class="ng-binding">{{ appinfos.count_hits }}</b></span>
<span class="bundleid ng-binding">BundleID<b class="ng-binding">&nbsp;&nbsp;{{ appinfos.bundle_id }}</b></span> <span class="bundleid ng-binding">BundleID<b class="ng-binding">&nbsp;&nbsp;{{ appinfos.bundle_id }}</b></span>
<span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span> <span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span>
<span class="short ng-scope" v-if="appinfos.issupersign">已经开启超级签</span> <span class="short ng-scope" v-if="appinfos.issupersign">超级签</span>
<el-popover <el-popover
placement="right" placement="right"
@ -53,11 +53,19 @@
<a class="" ref="combo" @click="combo"><i class="el-icon-copy-document" <a class="" ref="combo" @click="combo"><i class="el-icon-copy-document"
style="transform:rotateX(180deg);"></i>应用合并</a> style="transform:rotateX(180deg);"></i>应用合并</a>
</el-col> </el-col>
<el-col :span="3" <el-col :span="3"
v-if="(appinfos.type===1 && master_release.release_type ===1) || appinfos.issupersign "> v-if="(appinfos.type===1 && master_release.release_type ===1) || appinfos.issupersign ">
<a class="" ref="devices" @click="devices"><i class="el-icon-mobile-phone"></i>设备列表</a> <a class="" ref="devices" @click="devices"><i class="el-icon-mobile-phone"></i>设备列表</a>
</el-col> </el-col>
<el-col :span="3"
v-if="(appinfos.type===1 && master_release.release_type ===1) || appinfos.issupersign ">
<a class="" ref="supersign" @click="supersign"><i
class="el-icon-ship"></i>超级签名</a>
</el-col>
</el-row> </el-row>
</div> </div>
</div> </div>
@ -170,6 +178,10 @@
this.$router.push({name: 'FirAppInfosdevices'}); this.$router.push({name: 'FirAppInfosdevices'});
} }
}, },
supersign() {
this.setfunactive('supersign', 70);
this.$router.push({name: 'FirAppInfossupersign'});
},
}, created() { }, created() {
}, filters: { }, filters: {
@ -357,11 +369,11 @@
color: #4a4a4a color: #4a4a4a
} }
.page-app .tabs-container .el-row .el-col:nth-child(5) { .page-app .tabs-container .el-row .el-col:nth-child(6) {
display: none display: none
} }
.page-app .has-devices .tabs-container .el-row .el-col:nth-child(5) { .page-app .has-devices .tabs-container .el-row .el-col:nth-child(6) {
display: inline-block display: inline-block
} }

@ -49,26 +49,6 @@
<el-button @click="save_app_domain()">保存</el-button> <el-button @click="save_app_domain()">保存</el-button>
</el-form-item> </el-form-item>
<el-form-item v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active" label-width="200px"
label="自动超级签名">
<el-tooltip :content="supersign.msg" placement="top">
<el-switch
@change="supersignevent"
v-model="supersign.val"
active-color="#13ce66"
inactive-color="#ff4949"
active-value="on"
inactive-value="off">
</el-switch>
</el-tooltip>
<el-button @click="clean_app" v-if="currentapp.issupersign === 0 && currentapp.count !== 0"
style="margin-left: 20px" size="small" type="info" plain>清理开发者账户脏数据
</el-button>
<el-link v-else :underline="false" style="margin-left: 20px">超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link>
</el-form-item>
<el-form-item label-width="200px" label="微信内访问简易模式"> <el-form-item label-width="200px" label="微信内访问简易模式">
<el-tooltip :content="wxeasytypetip.msg" placement="top"> <el-tooltip :content="wxeasytypetip.msg" placement="top">
@ -119,16 +99,13 @@
currentapp: {}, currentapp: {},
orgcurrentapp: {}, orgcurrentapp: {},
downtip: {'msg': ''}, downtip: {'msg': ''},
supersign: {'msg': ''},
passwordtip: {'msg': ''}, passwordtip: {'msg': ''},
wxeasytypetip: {'msg': ''}, wxeasytypetip: {'msg': ''},
wxredirecttip: {'msg': ''}, wxredirecttip: {'msg': ''},
passwordflag: false, passwordflag: false,
showdownloadflag: false, showdownloadflag: false,
showsupersignflag: false,
wxeasytypeflag: false, wxeasytypeflag: false,
wxredirectflag: false, wxredirectflag: false,
clecount: 0,
wxeasy_disable: false, wxeasy_disable: false,
defualt_dtitle: '专属下载页域名', defualt_dtitle: '专属下载页域名',
} }
@ -137,14 +114,9 @@
set_default_flag() { set_default_flag() {
this.passwordflag = false; this.passwordflag = false;
this.showdownloadflag = false; this.showdownloadflag = false;
this.showsupersignflag = false;
this.wxeasytypeflag = false; this.wxeasytypeflag = false;
this.wxredirectflag = false; this.wxredirectflag = false;
}, },
clean_app() {
this.saveappinfo({"clean": true});
this.currentapp.count = 0;
},
save_app_domain() { save_app_domain() {
this.saveappinfo({ this.saveappinfo({
"domain_name": this.currentapp.domain_name, "domain_name": this.currentapp.domain_name,
@ -184,16 +156,6 @@
} }
this.showdownloadflag = true; this.showdownloadflag = true;
}, },
setbuttonsignshow(currentapp) {
if (currentapp.issupersign === 1) {
this.supersignevent("on");
this.supersign.val = 'on';
} else {
this.supersignevent("off");
this.supersign.val = 'off';
}
this.showsupersignflag = true;
},
setxeasytypeshow(currentapp) { setxeasytypeshow(currentapp) {
if (currentapp.wxeasytype === 1) { if (currentapp.wxeasytype === 1) {
this.wxeasytypeevent("on"); this.wxeasytypeevent("on");
@ -219,7 +181,6 @@
setbuttondefault(currentapp) { setbuttondefault(currentapp) {
this.setbuttondefaltpass(currentapp); this.setbuttondefaltpass(currentapp);
this.setbuttondefaltshow(currentapp); this.setbuttondefaltshow(currentapp);
this.setbuttonsignshow(currentapp);
this.setxeasytypeshow(currentapp); this.setxeasytypeshow(currentapp);
this.setwxredirectshow(currentapp); this.setwxredirectshow(currentapp);
}, },
@ -293,25 +254,6 @@
this.downtip.msg = '下载页不可见' this.downtip.msg = '下载页不可见'
} }
}, },
supersignevent(newval) {
if (newval === "on") {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 1,
});
this.currentapp.issupersign = 1;
}
this.supersign.msg = '已经开启超级签名';
} else {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 0,
});
this.currentapp.issupersign = 0;
}
this.supersign.msg = '关闭'
}
},
showpasswordevent(newval) { showpasswordevent(newval) {
if (newval === "on") { if (newval === "on") {
if (this.passwordflag) { if (this.passwordflag) {

@ -0,0 +1,205 @@
<template>
<div style="margin-top: 20px;width: 66%;margin-left: 8%">
<el-form label-width="80px">
<el-form-item v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active" label-width="200px"
label="超级签名">
<el-tooltip :content="supersign.msg" placement="top">
<el-switch
@change="supersignevent"
v-model="supersign.val"
active-color="#13ce66"
inactive-color="#ff4949"
active-value="on"
inactive-value="off">
</el-switch>
</el-tooltip>
<el-button @click="clean_app" v-if="currentapp.issupersign === 0 && currentapp.count !== 0"
style="margin-left: 20px" size="small" type="info" plain>清理开发者账户脏数据
</el-button>
<el-link v-else :underline="false" style="margin-left: 20px">超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link>
</el-form-item>
<el-form-item label-width="200px" label="签名类型"
v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active ">
<el-select v-model="currentapp.supersign_type" placeholder="特殊签名权限"
style="width: 60%;margin-right: 10px" :disabled="supersign_disable">
<el-option v-for="st in sign_type_list" :key="st.id" :label="st.name"
:value="st.id"></el-option>
</el-select>
<el-button @click="saveappinfo({supersign_type:currentapp.supersign_type})"
:disabled="supersign_disable"
>保存
</el-button>
</el-form-item>
<el-form-item label-width="200px" label="自定义BundleID"
v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active ">
<el-tooltip content="新的BundleID可能会导致推送等服务失效,请了解之后进行修改" placement="top">
<el-input v-model="currentapp.new_bundle_id" clearable :disabled="supersign_disable"
style="width: 60%;margin-right: 10px" prefix-icon="el-icon-s-data"
:placeholder="defualt_dtitle"/>
</el-tooltip>
<el-button @click="saveappinfo({new_bundle_id:currentapp.new_bundle_id})" :disabled="supersign_disable"
>保存
</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {apputils,} from "../restful"
import {deepCopy} from "../utils";
export default {
name: "FirAppInfossupersign",
data() {
return {
currentapp: {},
orgcurrentapp: {},
supersign: {'msg': ''},
showsupersignflag: false,
supersign_disable: true,
sign_type_list: [],
sign_type: 0,
defualt_dtitle: ''
}
},
methods: {
set_default_flag() {
this.showsupersignflag = false;
},
clean_app() {
this.saveappinfo({"clean": true});
this.currentapp.count = 0;
},
saveappinfo(data) {
apputils(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
} else {
this.$message.error('操作失败,' + data.msg);
this.$store.dispatch('doucurrentapp', this.orgcurrentapp);
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": data
});
},
setbuttonsignshow(currentapp) {
if (currentapp.issupersign === 1) {
this.supersignevent("on");
this.supersign.val = 'on';
} else {
this.supersignevent("off");
this.supersign.val = 'off';
}
this.showsupersignflag = true;
},
setbuttondefault(currentapp) {
this.setbuttonsignshow(currentapp);
},
supersignevent(newval) {
if (newval === "on") {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 1,
});
this.currentapp.issupersign = 1;
}
this.supersign.msg = '已经开启超级签名';
this.supersign_disable = false
} else {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 0,
});
this.currentapp.issupersign = 0;
}
this.supersign.msg = '关闭';
this.supersign_disable = true
}
},
set_default_ms(currentapp) {
this.sign_type_list = currentapp.sign_type_choice;
if (this.currentapp.new_bundle_id && currentapp.new_bundle_id.length > 5) {
this.defualt_dtitle = currentapp.new_bundle_id
} else {
this.defualt_dtitle = currentapp.bundle_id
}
},
appinit() {
this.currentapp = this.$store.state.currentapp;
this.set_default_flag();
this.orgcurrentapp = deepCopy(this.currentapp);
this.set_default_ms(this.currentapp);
this.setbuttondefault(this.currentapp);
}
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[70, 70], [70, 70]]);
if (!this.currentapp.app_id) {
this.appinit();
}
},
watch: {
'$store.state.currentapp': function () {
this.appinit();
},
'currentapp.new_bundle_id': function () {
if (!this.currentapp.new_bundle_id || this.currentapp.new_bundle_id.length === 0) {
this.defualt_dtitle = this.currentapp.bundle_id
}
}
}, computed: {}
}
</script>
<style scoped>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
height: 100px;
width: 100px;
border-radius: 10px;
display: block;
}
</style>

@ -96,6 +96,13 @@ const router = new VueRouter({
// component: FirAppInfoscombo, // component: FirAppInfoscombo,
component: () => import("@/components/FirAppInfoscombo"), component: () => import("@/components/FirAppInfoscombo"),
},
{
path: 'supersign',
name: 'FirAppInfossupersign',
// component: FirAppInfossupersign,
component: () => import("@/components/FirAppInfossupersign"),
} }
] ]
} }

@ -0,0 +1,22 @@
# Generated by Django 3.0.3 on 2021-03-08 13:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0017_auto_20210131_2108'),
]
operations = [
migrations.AddField(
model_name='apps',
name='enable_push_vpn',
field=models.BigIntegerField(default=False, verbose_name='特殊权限(包含network、vpn)'),
),
migrations.AddField(
model_name='apps',
name='new_bundle_id',
field=models.CharField(blank=True, help_text='用与超级签某些因素下修改包名', max_length=64, verbose_name='new_bundle_id'),
),
]

@ -0,0 +1,22 @@
# Generated by Django 3.0.3 on 2021-03-08 13:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0018_auto_20210308_1352'),
]
operations = [
migrations.RemoveField(
model_name='apps',
name='enable_push_vpn',
),
migrations.AddField(
model_name='apps',
name='supersign_type',
field=models.SmallIntegerField(choices=[(0, '普通权限'), (1, '特殊权限(包含network、vpn)')], default=0,
verbose_name='签名类型'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2021-03-08 14:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0019_auto_20210308_1355'),
]
operations = [
migrations.AlterField(
model_name='apps',
name='new_bundle_id',
field=models.CharField(blank=True, help_text='用与超级签某些因素下修改包名', max_length=64, null=True,
verbose_name='new_bundle_id'),
),
]

@ -113,6 +113,10 @@ class Apps(models.Model):
password = models.CharField(verbose_name="访问密码", default='', help_text='默认 没有密码', max_length=32) password = models.CharField(verbose_name="访问密码", default='', help_text='默认 没有密码', max_length=32)
isshow = models.BigIntegerField(verbose_name="下载页可见", default=1) isshow = models.BigIntegerField(verbose_name="下载页可见", default=1)
issupersign = models.BigIntegerField(verbose_name="是否超级签名包", default=False) issupersign = models.BigIntegerField(verbose_name="是否超级签名包", default=False)
supersign_type_choices = ((0, '普通权限'), (1, '特殊权限(包含network、vpn)'))
supersign_type = models.SmallIntegerField(choices=supersign_type_choices, default=0, verbose_name="签名类型")
new_bundle_id = models.CharField(max_length=64, blank=True, null=True, verbose_name="new_bundle_id",
help_text="用于超级签某些因素下需要修改包名")
wxredirect = models.BigIntegerField(verbose_name="微信内第三方链接自动跳转", default=True) wxredirect = models.BigIntegerField(verbose_name="微信内第三方链接自动跳转", default=True)
wxeasytype = models.BigIntegerField(verbose_name="微信内简易模式,避免微信封停", default=True) wxeasytype = models.BigIntegerField(verbose_name="微信内简易模式,避免微信封停", default=True)
domain_name = models.CharField(verbose_name="专属访问域名", blank=True, null=True, max_length=64) domain_name = models.CharField(verbose_name="专属访问域名", blank=True, null=True, max_length=64)

@ -124,9 +124,14 @@ class ResignApp(object):
mobilconfig_path, sign_mobilconfig_path, ssl_pem_path, ssl_key_path, ssl_pem_path) mobilconfig_path, sign_mobilconfig_path, ssl_pem_path, ssl_key_path, ssl_pem_path)
return exec_shell(cmd) return exec_shell(cmd)
def sign(self, new_profile, org_ipa, new_ipa): def sign(self, new_profile, org_ipa, new_ipa, info_plist_properties=None):
self.cmd = self.cmd + " -p '%s' -o '%s' '%s'" % (new_profile, new_ipa, org_ipa) if info_plist_properties is None:
result = exec_shell(self.cmd) info_plist_properties = {}
properties = ""
for k, v in info_plist_properties.items():
properties += " %s=%s " % (k, v)
self.cmd = self.cmd + " -i '%s' -p '%s' -o '%s' '%s'" % (properties, new_profile, new_ipa, org_ipa)
return exec_shell(self.cmd)
class AppDeveloperApiV2(object): class AppDeveloperApiV2(object):
@ -205,9 +210,11 @@ class AppDeveloperApiV2(object):
result['return_info'] = "%s" % e result['return_info'] = "%s" % e
return False, result return False, result
def get_profile(self, bundleId, app_id, device_udid, device_name, provisionName, auth, developer_app_id, def get_profile(self, app_obj, udid_info, provisionName, auth, developer_app_id,
device_id_list): device_id_list):
result = {} result = {}
bundle_id = app_obj.bundleId
app_id = app_obj.app_id
try: try:
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key) apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
if developer_app_id: if developer_app_id:
@ -215,24 +222,30 @@ class AppDeveloperApiV2(object):
# bundle_obj = apple_obj.register_bundle_id_enable_capability(app_id, bundleId + app_id) # bundle_obj = apple_obj.register_bundle_id_enable_capability(app_id, bundleId + app_id)
else: else:
# bundle_obj = apple_obj.list_bundle_ids_by_identifier(bundleId + app_id) # bundle_obj = apple_obj.list_bundle_ids_by_identifier(bundleId + app_id)
bundle_obj = apple_obj.register_bundle_id_enable_capability(app_id, bundleId + app_id) if app_obj.supersign_type == 0:
bundle_obj = apple_obj.register_bundle_id(app_id, bundle_id + app_id)
else:
bundle_obj = apple_obj.register_bundle_id_enable_capability(app_id, bundle_id + app_id)
developer_app_id = bundle_obj.id developer_app_id = bundle_obj.id
result['aid'] = developer_app_id result['aid'] = developer_app_id
if udid_info:
device_obj = apple_obj.register_device(device_name, device_udid) device_udid = udid_info.get('udid')
if device_obj: device_name = udid_info.get('product')
result['did'] = device_obj.id device_obj = apple_obj.register_device(device_name, device_udid)
device_id_list.append(device_obj.id) if device_obj:
profile_obj = apple_obj.create_profile(developer_app_id, auth.get('certid'), result['did'] = device_obj.id
provisionName.split("/")[-1], device_id_list.append(device_obj.id)
device_id_list)
if profile_obj: profile_obj = apple_obj.create_profile(developer_app_id, auth.get('certid'),
n = base64.b64decode(profile_obj.profileContent) provisionName.split("/")[-1],
if not os.path.isdir(os.path.dirname(provisionName)): device_id_list)
os.makedirs(os.path.dirname(provisionName)) if profile_obj:
with open(provisionName, 'wb') as f: n = base64.b64decode(profile_obj.profileContent)
f.write(n) if not os.path.isdir(os.path.dirname(provisionName)):
return True, result os.makedirs(os.path.dirname(provisionName))
with open(provisionName, 'wb') as f:
f.write(n)
return True, result
except Exception as e: except Exception as e:
logger.error("ios developer make profile Failed Exception:%s" % e) logger.error("ios developer make profile Failed Exception:%s" % e)
result['return_info'] = "%s" % e result['return_info'] = "%s" % e
@ -304,3 +317,27 @@ class AppDeveloperApiV2(object):
logger.error("ios developer create app Failed Exception:%s" % e) logger.error("ios developer create app Failed Exception:%s" % e)
result['return_info'] = "%s" % e result['return_info'] = "%s" % e
return False, result return False, result
def modify_capability(self, app_obj, developer_app_id):
bundle_id = app_obj.bundleId
app_id = app_obj.app_id
result = {}
try:
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
if developer_app_id:
if app_obj.supersign_type == 0:
result['code'] = apple_obj.disable_all_capability(developer_app_id)
else:
result['code'] = apple_obj.enable_push_vpn_capability(developer_app_id)
else:
if app_obj.supersign_type == 0:
bundle_obj = apple_obj.register_bundle_id(app_id, bundle_id + app_id)
else:
bundle_obj = apple_obj.register_bundle_id_enable_capability(app_id, bundle_id + app_id)
developer_app_id = bundle_obj.id
result['aid'] = developer_app_id
return True, result
except Exception as e:
logger.error("ios developer modify_capability Failed Exception:%s" % e)
result['return_info'] = "%s" % e
return False, result

@ -14,11 +14,26 @@ from api.utils.serializer import get_developer_udided
from api.utils.storage.localApi import LocalStorage from api.utils.storage.localApi import LocalStorage
from api.utils.storage.caches import del_cache_response_by_short, send_msg_over_limit from api.utils.storage.caches import del_cache_response_by_short, send_msg_over_limit
from api.utils.utils import file_format_path, delete_app_to_dev_and_file, delete_app_profile_file, \ from api.utils.utils import file_format_path, delete_app_to_dev_and_file, delete_app_profile_file, \
send_ios_developer_active_status send_ios_developer_active_status, get_profile_full_path
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
def resign_by_app_obj(app_obj, need_download_profile=True):
user_obj = app_obj.user_id
if app_obj.issupersign and user_obj.supersign_active:
for dappid_obj in DeveloperAppID.objects.filter(app_id=app_obj).all():
developer_obj = dappid_obj.developerid
developer_app_id = dappid_obj.aid
if need_download_profile:
IosUtils.modify_capability(developer_obj, app_obj, developer_app_id)
download_flag, result = IosUtils.exec_download_profile(app_obj, developer_obj, 2)
else:
download_flag = True
IosUtils.run_sign(user_obj, app_obj, developer_obj, download_flag, None,
resign=True)
def udid_bytes_to_dict(xml_stream): def udid_bytes_to_dict(xml_stream):
new_uuid_info = {} new_uuid_info = {}
try: try:
@ -247,11 +262,7 @@ class IosUtils(object):
return developer_obj return developer_obj
def download_profile(self, developer_app_id, device_id_list): def download_profile(self, developer_app_id, device_id_list):
bundleId = self.app_obj.bundle_id return get_api_obj(self.auth).get_profile(self.app_obj, self.udid_info,
app_id = self.app_obj.app_id
device_udid = self.udid_info.get('udid')
device_name = self.udid_info.get('product')
return get_api_obj(self.auth).get_profile(bundleId, app_id, device_udid, device_name,
self.get_profile_full_path(), self.get_profile_full_path(),
self.auth, developer_app_id, device_id_list) self.auth, developer_app_id, device_id_list)
@ -261,75 +272,77 @@ class IosUtils(object):
app_id = self.app_obj.app_id app_id = self.app_obj.app_id
return get_api_obj(self.auth).create_app(bundleId, app_id) return get_api_obj(self.auth).create_app(bundleId, app_id)
@staticmethod
def modify_capability(developer_obj, app_obj, developer_app_id):
auth = get_auth_form_developer(developer_obj)
return get_api_obj(auth).modify_capability(app_obj, developer_app_id)
def get_profile_full_path(self): def get_profile_full_path(self):
cert_dir_name = make_app_uuid(self.user_obj, get_apple_udid_key(self.auth)) cert_dir_name = make_app_uuid(self.user_obj, get_apple_udid_key(self.auth))
cert_dir_path = os.path.join(SUPER_SIGN_ROOT, cert_dir_name, "profile") cert_dir_path = os.path.join(SUPER_SIGN_ROOT, cert_dir_name, "profile")
if not os.path.isdir(cert_dir_path): if not os.path.isdir(cert_dir_path):
os.makedirs(cert_dir_path) os.makedirs(cert_dir_path)
provisionName = os.path.join(cert_dir_path, self.app_obj.app_id) provision_name = os.path.join(cert_dir_path, self.app_obj.app_id)
return provisionName + '.mobileprovision' return provision_name + '.mobileprovision'
def resign(self): @staticmethod
if not self.developer_obj: def exec_download_profile(app_obj, developer_obj, sign_try_attempts=3):
logger.error("udid %s app %s not exists apple developer" % (self.udid_info.get('udid'), self.app_obj))
return
app_udid_obj = AppUDID.objects.filter(app_id=self.app_obj, udid=self.udid_info.get('udid')).first()
if app_udid_obj and app_udid_obj.is_signed:
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
result = {} result = {}
while fcount > 0: developer_app_id = None
# apptodev_obj = APPToDeveloper.objects.filter(developerid=self.developer_obj, app_id=self.app_obj).first() auth = get_auth_form_developer(developer_obj)
device_id_list = DeveloperDevicesID.objects.filter(app_id=self.app_obj, while sign_try_attempts > 0:
developerid=self.developer_obj).values_list('did') device_id_list = DeveloperDevicesID.objects.filter(app_id=app_obj,
developerid=developer_obj).values_list('did')
developer_app_id = None developer_app_id = None
developer_appid_obj = DeveloperAppID.objects.filter(developerid=self.developer_obj, developer_appid_obj = DeveloperAppID.objects.filter(developerid=developer_obj,
app_id=self.app_obj).first() app_id=app_obj).first()
if developer_appid_obj: if developer_appid_obj:
developer_app_id = developer_appid_obj.aid developer_app_id = developer_appid_obj.aid
status, result = self.download_profile(developer_app_id, [did[0] for did in device_id_list])
status, result = get_api_obj(auth).get_profile(app_obj, None,
get_profile_full_path(developer_obj, app_obj),
auth, developer_app_id, [did[0] for did in device_id_list])
if not status: if not status:
fcount -= 1 sign_try_attempts -= 1
logger.warning("udid %s app %s developer %s sign failed %s .try again " % ( logger.warning("app %s developer %s sign failed %s .try again " % (app_obj, developer_obj, result))
self.udid_info.get('udid'), self.app_obj, self.developer_obj, result))
time.sleep(3) time.sleep(3)
else: else:
fcount = -1 sign_try_attempts = -1
if fcount != -1: if sign_try_attempts != -1:
logger.error("udid %s app %s developer %s sign failed %s" % ( logger.error("app %s developer %s sign failed %s" % (app_obj, developer_obj, result))
self.udid_info.get('udid'), self.app_obj, self.developer_obj, result)) developer_obj.is_actived = False
self.developer_obj.is_actived = False developer_obj.save()
self.developer_obj.save() send_ios_developer_active_status(developer_obj.user_id,
send_ios_developer_active_status(self.developer_obj.user_id,
'app %s developer %s sign failed %s. disable this developer' % ( 'app %s developer %s sign failed %s. disable this developer' % (
self.app_obj, self.developer_obj, result)) app_obj, developer_obj, result))
self.get_developer_auth() return False, result
self.resign()
return if not developer_app_id and result.get("aid", None):
DeveloperAppID.objects.create(aid=result["aid"], developerid=developer_obj, app_id=app_obj)
AppUDID.objects.update_or_create(app_id=self.app_obj, udid=self.udid_info.get('udid'), return True, result
defaults=self.udid_info)
file_format_path_name = file_format_path(self.user_obj, self.auth) @staticmethod
def exec_sign(user_obj, app_obj, developer_obj, random_file_name, release_obj):
auth = get_auth_form_developer(developer_obj)
file_format_path_name = file_format_path(user_obj, auth)
my_local_key = file_format_path_name + ".key" my_local_key = file_format_path_name + ".key"
app_dev_pem = file_format_path_name + ".pem" app_dev_pem = file_format_path_name + ".pem"
ResignAppObj = ResignApp(my_local_key, app_dev_pem) resign_app_obj = ResignApp(my_local_key, app_dev_pem)
random_file_name = make_from_user_uuid(self.user_obj)
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") org_file = os.path.join(MEDIA_ROOT, release_obj.release_id + ".ipa")
new_file = os.path.join(MEDIA_ROOT, random_file_name + ".ipa") new_file = os.path.join(MEDIA_ROOT, random_file_name + ".ipa")
ResignAppObj.sign(self.get_profile_full_path(), org_file, new_file) status, result = resign_app_obj.sign(get_profile_full_path(developer_obj, app_obj), org_file, new_file,
app_obj.new_bundle_id)
if status:
logger.info("%s %s %s sign_ipa success" % (user_obj, developer_obj, app_obj))
return True
else:
logger.error(
"%s %s %s sign_ipa failed ERROR:%s" % (user_obj, developer_obj, app_obj, result.get("err_info")))
return False
def update_sign_data(self, random_file_name, release_obj, result):
newdata = { newdata = {
"is_signed": True, "is_signed": True,
"binary_file": random_file_name "binary_file": random_file_name
@ -385,8 +398,59 @@ class IosUtils(object):
APPToDeveloper.objects.create(developerid=self.developer_obj, app_id=self.app_obj, APPToDeveloper.objects.create(developerid=self.developer_obj, app_id=self.app_obj,
binary_file=random_file_name, release_file=release_obj.release_id) binary_file=random_file_name, release_file=release_obj.release_id)
if not developer_app_id and result.get("aid", None): # if not developer_app_id and result.get("aid", None):
DeveloperAppID.objects.create(aid=result["aid"], developerid=self.developer_obj, app_id=self.app_obj) # DeveloperAppID.objects.create(aid=result["aid"], developerid=self.developer_obj, app_id=self.app_obj)
@staticmethod
def run_sign(user_obj, app_obj, developer_obj, download_flag, obj, resign=False):
result = {}
start_time = time.time()
d_time1 = time.time()
if download_flag:
logger.info("app_id %s download profile success. time:%s" % (app_obj, d_time1 - start_time))
random_file_name = make_from_user_uuid(user_obj)
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
if IosUtils.exec_sign(user_obj, app_obj, developer_obj, random_file_name, release_obj):
s_time1 = time.time()
logger.info("app_id %s exec sign ipa success. time:%s" % (app_obj, s_time1 - d_time1))
if not resign:
obj.update_sign_data(random_file_name, release_obj, result)
else:
logger.error("app_id %s download profile failed. %s time:%s" % (app_obj, result, d_time1 - start_time))
return False
logger.info(
"app_id %s developer %s sign end... time:%s" % (app_obj, developer_obj, time.time() - start_time))
return True
def sign(self, sign_try_attempts=3, resign=False):
if not self.developer_obj:
logger.error("udid %s app %s not exists apple developer" % (self.udid_info.get('udid'), self.app_obj))
return
if not resign:
app_udid_obj = AppUDID.objects.filter(app_id=self.app_obj, udid=self.udid_info.get('udid')).first()
if app_udid_obj and app_udid_obj.is_signed:
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))
else:
logger.info("resign app_id %s ,need resign" % self.app_obj)
call_flag = True
download_flag = False
while call_flag:
if self.developer_obj:
download_flag, result = IosUtils.exec_download_profile(self.app_obj, self.developer_obj,
sign_try_attempts)
if not download_flag:
self.get_developer_auth()
else:
call_flag = False
IosUtils.run_sign(self.user_obj, self.app_obj, self.developer_obj, download_flag, self, resign=False)
@staticmethod @staticmethod
def disable_udid(udid_obj, app_id): def disable_udid(udid_obj, app_id):

@ -731,7 +731,7 @@ class AppStoreConnectApi(DevicesAPI, BundleIDsAPI, BundleIDsCapabilityAPI, Profi
return True return True
return False return False
def __enable_capability(self, bundle_id): def enable_push_vpn_capability(self, bundle_id):
# 'PUSH_NOTIFICATIONS', # PERSONAL_VPN # 'PUSH_NOTIFICATIONS', # PERSONAL_VPN
req = super().enable_capability(bundle_id, 'PUSH_NOTIFICATIONS') req = super().enable_capability(bundle_id, 'PUSH_NOTIFICATIONS')
if self.__do_success(req, 201): if self.__do_success(req, 201):
@ -740,7 +740,14 @@ class AppStoreConnectApi(DevicesAPI, BundleIDsAPI, BundleIDsCapabilityAPI, Profi
return True return True
return False return False
def __register_bundle_id(self, bundle_id_name, bundle_id_identifier, platform="IOS", seed_id=''): def disable_all_capability(self, bundle_id):
# 'PUSH_NOTIFICATIONS', # PERSONAL_VPN
req = super().disable_capability(bundle_id)
if self.__do_success(req, 204):
return True
return False
def register_bundle_id(self, bundle_id_name, bundle_id_identifier, platform="IOS", seed_id=''):
identifier_obj = self.list_bundle_ids_by_identifier(bundle_id_identifier) identifier_obj = self.list_bundle_ids_by_identifier(bundle_id_identifier)
if isinstance(identifier_obj, BundleIds): if isinstance(identifier_obj, BundleIds):
req = self.modify_bundle_id(identifier_obj.id, bundle_id_name) req = self.modify_bundle_id(identifier_obj.id, bundle_id_name)
@ -750,9 +757,9 @@ class AppStoreConnectApi(DevicesAPI, BundleIDsAPI, BundleIDsCapabilityAPI, Profi
return self.__bundle_ids_store(req, 201) return self.__bundle_ids_store(req, 201)
def register_bundle_id_enable_capability(self, bundle_id_name, bundle_id_identifier, platform="IOS", seed_id=''): def register_bundle_id_enable_capability(self, bundle_id_name, bundle_id_identifier, platform="IOS", seed_id=''):
bundle_ids = self.__register_bundle_id(bundle_id_name, bundle_id_identifier, platform, seed_id) bundle_ids = self.register_bundle_id(bundle_id_name, bundle_id_identifier, platform, seed_id)
if isinstance(bundle_ids, BundleIds): if isinstance(bundle_ids, BundleIds):
if self.__enable_capability(bundle_ids.id): if self.enable_push_vpn_capability(bundle_ids.id):
return bundle_ids return bundle_ids
def delete_bundle_by_identifier(self, identifier): def delete_bundle_by_identifier(self, identifier):

@ -39,6 +39,14 @@ class AppsSerializer(serializers.ModelSerializer):
obj.has_combo.has_combo = None obj.has_combo.has_combo = None
return AppsSerializer(obj.has_combo, context=self.context).data return AppsSerializer(obj.has_combo, context=self.context).data
sign_type_choice = serializers.SerializerMethodField()
def get_sign_type_choice(self, obj):
sign_type_choice = []
for auth_t in list(obj.supersign_type_choices):
sign_type_choice.append({'id': auth_t[0], 'name': auth_t[1]})
return sign_type_choice
master_release = serializers.SerializerMethodField() master_release = serializers.SerializerMethodField()
def get_master_release(self, obj): def get_master_release(self, obj):

@ -44,8 +44,8 @@ def get_profile_full_path(developer_obj, app_obj):
pkey = developer_obj.issuer_id pkey = developer_obj.issuer_id
cert_dir_name = make_app_uuid(developer_obj.user_id, pkey) cert_dir_name = make_app_uuid(developer_obj.user_id, pkey)
cert_dir_path = os.path.join(SUPER_SIGN_ROOT, cert_dir_name, "profile") cert_dir_path = os.path.join(SUPER_SIGN_ROOT, cert_dir_name, "profile")
provisionName = os.path.join(cert_dir_path, app_obj.app_id) provision_name = os.path.join(cert_dir_path, app_obj.app_id)
return provisionName + '.mobileprovision' return provision_name + '.mobileprovision'
def delete_app_to_dev_and_file(developer_obj, app_id): def delete_app_to_dev_and_file(developer_obj, app_id):

@ -9,7 +9,7 @@ from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication from api.utils.auth import ExpiringTokenAuthentication
from rest_framework.response import Response from rest_framework.response import Response
from django.db.models import Sum from django.db.models import Sum
from api.utils.app.supersignutils import IosUtils from api.utils.app.supersignutils import IosUtils, resign_by_app_obj
from api.utils.storage.storage import Storage from api.utils.storage.storage import Storage
from api.utils.storage.caches import del_cache_response_by_short, get_app_today_download_times, del_cache_by_delete_app from api.utils.storage.caches import del_cache_response_by_short, get_app_today_download_times, del_cache_by_delete_app
from api.models import Apps, AppReleaseInfo, APPToDeveloper, AppIOSDeveloperInfo, UserInfo from api.models import Apps, AppReleaseInfo, APPToDeveloper, AppIOSDeveloperInfo, UserInfo
@ -225,12 +225,33 @@ class AppInfoView(APIView):
apps_obj.name = data.get("name", apps_obj.name) apps_obj.name = data.get("name", apps_obj.name)
apps_obj.password = data.get("password", apps_obj.password) apps_obj.password = data.get("password", apps_obj.password)
apps_obj.isshow = data.get("isshow", apps_obj.isshow) apps_obj.isshow = data.get("isshow", apps_obj.isshow)
apps_obj.domain_name = data.get("domain_name", apps_obj.domain_name) domain_name = data.get("domain_name", None)
if request.user.domain_name and len(request.user.domain_name) > 3: if domain_name:
domain_name_list = domain_name.strip(' ').replace("http://", "").replace("https://", "").split(
"/")
if len(domain_name_list) > 0:
domain_name = domain_name_list[0]
if len(domain_name) > 3 and is_valid_domain(domain_name):
apps_obj.domain_name = data.get("domain_name", apps_obj.domain_name)
if domain_name == '':
apps_obj.domain_name = None
if (request.user.domain_name and len(request.user.domain_name) > 3) or (
apps_obj.domain_name and len(apps_obj.domain_name) > 3):
apps_obj.wxeasytype = data.get("wxeasytype", apps_obj.wxeasytype) apps_obj.wxeasytype = data.get("wxeasytype", apps_obj.wxeasytype)
else: else:
apps_obj.wxeasytype = 1 apps_obj.wxeasytype = 1
if apps_obj.issupersign:
if apps_obj.supersign_type in [x[0] for x in list(apps_obj.supersign_type_choices)]:
apps_obj.supersign_type = data.get("supersign_type", apps_obj.supersign_type)
new_bundle_id = data.get("new_bundle_id", None)
if new_bundle_id and new_bundle_id != apps_obj.bundle_id and len(new_bundle_id) > 3:
apps_obj.new_bundle_id = new_bundle_id
if new_bundle_id == '':
apps_obj.new_bundle_id = None
apps_obj.wxredirect = data.get("wxredirect", apps_obj.wxredirect) apps_obj.wxredirect = data.get("wxredirect", apps_obj.wxredirect)
if apps_obj.type == 1 and data.get('issupersign', -1) != -1: if apps_obj.type == 1 and data.get('issupersign', -1) != -1:
# 为啥注释掉,就是该udid已经在该平台使用了,虽然已经没有余额,但是其他应用也是可以超级签名的 # 为啥注释掉,就是该udid已经在该平台使用了,虽然已经没有余额,但是其他应用也是可以超级签名的
@ -249,6 +270,12 @@ class AppInfoView(APIView):
apps_obj.issupersign = data.get("issupersign", apps_obj.issupersign) apps_obj.issupersign = data.get("issupersign", apps_obj.issupersign)
logger.info("app_id:%s update new data:%s" % (app_id, apps_obj.__dict__)) logger.info("app_id:%s update new data:%s" % (app_id, apps_obj.__dict__))
apps_obj.save() apps_obj.save()
if apps_obj.issupersign:
if apps_obj.supersign_type != data.get("supersign_type", apps_obj.supersign_type):
resign_by_app_obj(apps_obj)
if apps_obj.new_bundle_id != data.get("new_bundle_id", apps_obj.new_bundle_id):
resign_by_app_obj(apps_obj, need_download_profile=False)
del_cache_response_by_short(apps_obj.app_id) del_cache_response_by_short(apps_obj.app_id)
except Exception as e: except Exception as e:
logger.error("app_id:%s update Exception:%s" % (app_id, e)) logger.error("app_id:%s update Exception:%s" % (app_id, e))

@ -26,7 +26,7 @@ class IosUDIDView(View):
if app_info: if app_info:
if app_info.issupersign and app_info.user_id.supersign_active: if app_info.issupersign and app_info.user_id.supersign_active:
ios_obj = IosUtils(format_udid_info, app_info.user_id, app_info) ios_obj = IosUtils(format_udid_info, app_info.user_id, app_info)
ios_obj.resign() ios_obj.sign()
else: else:
return HttpResponsePermanentRedirect( return HttpResponsePermanentRedirect(
"%s/%s" % (server_domain, short)) "%s/%s" % (server_domain, short))

@ -7,7 +7,7 @@
from api.utils.app.apputils import get_random_short, SaveAppInfos from api.utils.app.apputils import get_random_short, SaveAppInfos
from api.utils.storage.storage import Storage from api.utils.storage.storage import Storage
from api.utils.storage.caches import upload_file_tmp_name, del_cache_response_by_short from api.utils.storage.caches import upload_file_tmp_name, del_cache_response_by_short
from api.models import Apps, AppReleaseInfo, UserInfo, AppUDID from api.models import Apps, AppReleaseInfo, UserInfo
from api.utils.app.randomstrings import make_app_uuid from api.utils.app.randomstrings import make_app_uuid
from rest_framework.views import APIView from rest_framework.views import APIView
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
@ -16,7 +16,7 @@ from api.utils.app.randomstrings import make_from_user_uuid
from rest_framework.response import Response from rest_framework.response import Response
from fir_ser import settings from fir_ser import settings
from api.utils.TokenManager import DownloadToken from api.utils.TokenManager import DownloadToken
from api.utils.app.supersignutils import IosUtils from api.utils.app.supersignutils import resign_by_app_obj
import os, json, logging import os, json, logging
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
@ -121,13 +121,7 @@ class AppAnalyseView(APIView):
app_info = Apps.objects.filter(bundle_id=data.get("bundleid")).first() app_info = Apps.objects.filter(bundle_id=data.get("bundleid")).first()
if app_info: if app_info:
if app_info.issupersign and app_info.user_id.supersign_active: if app_info.issupersign and app_info.user_id.supersign_active:
udid_obj = AppUDID.objects.filter(app_id=app_info).first() resign_by_app_obj(app_info)
if udid_obj:
udid_info = {'imei': udid_obj.imei, 'product': udid_obj.product,
'serial': udid_obj.serial, 'udid': udid_obj.udid,
'version': udid_obj.version}
ios_obj = IosUtils(udid_info, app_info.user_id, app_info)
ios_obj.resign()
else: else:
storage.delete_file(app_tmp_filename) storage.delete_file(app_tmp_filename)

@ -37,7 +37,7 @@ udid_lists = [
def postaaa(shorts='bncz'): def postaaa(shorts='bncz'):
for udid in udid_lists: for udid in udid_lists:
udid = udid + str(random.choice(short)) + str(random.choice(short)) + str(random.choice(short))+shorts udid = udid + str(random.choice(short)) + str(random.choice(short)) + str(random.choice(short)) + shorts
data = '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n\t<key>PRODUCT</key>\n\t<string>iPhone9,1</string>\n\t<key>SERIAL</key>\n\t<string>F71YD58GHG74</string>\n\t<key>UDID</key>\n\t<string>%s</string>\n\t<key>VERSION</key>\n\t<string>17E262</string>\n</dict>\n</plist>\n' % ( data = '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0">\n<dict>\n\t<key>PRODUCT</key>\n\t<string>iPhone9,1</string>\n\t<key>SERIAL</key>\n\t<string>F71YD58GHG74</string>\n\t<key>UDID</key>\n\t<string>%s</string>\n\t<key>VERSION</key>\n\t<string>17E262</string>\n</dict>\n</plist>\n' % (
udid) udid)
# uri = "https://app.hehelucky.cn/udid/%s" % (random.choice(short)) # uri = "https://app.hehelucky.cn/udid/%s" % (random.choice(short))

Loading…
Cancel
Save