增加支持p12 证书导入

pull/28/head
youngS 3 years ago
parent e6f8b2f628
commit b40f93a16c
  1. 107
      fir_client/src/components/user/FirSuperSignBase.vue
  2. 56
      fir_client/src/restful/index.js
  3. 3
      fir_ser/api/urls.py
  4. 88
      fir_ser/api/utils/app/iossignapi.py
  5. 79
      fir_ser/api/utils/app/supersignutils.py
  6. 6
      fir_ser/api/utils/apple/appleapiv3.py
  7. 7
      fir_ser/api/utils/baseutils.py
  8. 20
      fir_ser/api/utils/utils.py
  9. 61
      fir_ser/api/views/supersign.py

@ -1,13 +1,37 @@
<template> <template>
<el-main> <el-main>
<el-dialog title="发布证书导入" :visible.sync="importcertDeveloperVisible" :destroy-on-close="true"
:close-on-click-modal="false" style="text-align:center">
<el-form label-width="30%">
<el-form-item label="发布证书p12文件" style="width: 80%">
<el-upload
drag
ref="upload"
action="#"
:before-upload="beforeAvatarUpload"
accept=".p12"
:file-list="fileList"
:auto-upload="false"
:limit="1"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将p12证书文件拖到此处<em>点击上传</em></div>
</el-upload>
</el-form-item>
<el-form-item label="发布证书p12密码" style="width: 80%">
<el-input label="证书密码" v-model="import_cert_info.cert_pwd"></el-input>
</el-form-item>
<el-button @click="$refs.upload.submit()">确定导入</el-button>
<el-button @click="importcertDeveloperVisible=false">取消</el-button>
</el-form>
</el-dialog>
<el-dialog :title="title" :visible.sync="dialogaddDeveloperVisible" :destroy-on-close="true" <el-dialog :title="title" :visible.sync="dialogaddDeveloperVisible" :destroy-on-close="true"
:close-on-click-modal="false" style="text-align:center"> :close-on-click-modal="false" style="text-align:center">
<el-form ref="storageinfoform" :model="editdeveloperinfo" <el-form ref="storageinfoform" :model="editdeveloperinfo"
label-width="80px" style="margin:0 auto;"> label-width="80px" style="margin:0 auto;">
<div v-if="editdeveloperinfo.auth_type===0"> <div v-if="editdeveloperinfo.auth_type===0">
<el-form-item label-width="110px" label="issuer_id"> <el-form-item label-width="110px" label="issuer_id">
<el-input :disabled='isedit' v-model="editdeveloperinfo.issuer_id"/> <el-input :disabled='isedit' v-model="editdeveloperinfo.issuer_id"/>
@ -26,16 +50,30 @@
<el-form-item label-width="110px" label="设备数量" style="text-align: left"> <el-form-item label-width="110px" label="设备数量" style="text-align: left">
<el-input-number v-model="editdeveloperinfo.usable_number" :min="0" :max="100" label="设备数量"/> <el-input-number v-model="editdeveloperinfo.usable_number" :min="0" :max="100" label="设备数量"/>
</el-form-item> </el-form-item>
<el-form-item label-width="110px" label="证书id">
<el-input :disabled='isedit' v-model="editdeveloperinfo.certid"/>
</el-form-item>
<el-form-item label-width="110px" label="备注"> <el-form-item label-width="110px" label="备注">
<el-input v-model="editdeveloperinfo.description"/> <el-input v-model="editdeveloperinfo.description"/>
</el-form-item> </el-form-item>
<div style=""> <div style="">
<el-button v-if="isedit && editdeveloperinfo.is_actived && editdeveloperinfo.certid" size="small"
@click="exportcert">导出证书
</el-button>
<el-button v-if="isedit && editdeveloperinfo.is_actived && !editdeveloperinfo.certid" size="small"
@click="importcertDeveloperVisible=true">导入p12证书
</el-button>
<el-button v-if="isedit && editdeveloperinfo.is_actived" size="small" @click="syncdevices">同步设备信息 <el-button v-if="isedit && editdeveloperinfo.is_actived" size="small" @click="syncdevices">同步设备信息
</el-button> </el-button>
<el-button v-if="isedit && editdeveloperinfo.is_actived && !editdeveloperinfo.certid" size="small" <el-button v-if="isedit && editdeveloperinfo.is_actived && !editdeveloperinfo.certid" size="small"
@click="isocertcert">手动创建证书 @click="isocertcert">手动创建发布证书
</el-button> </el-button>
<el-tooltip content="发布证书只能创建两个,请谨慎操作">
<el-button v-if="isedit && editdeveloperinfo.is_actived && editdeveloperinfo.certid"
size="small"
@click="isorenewcert">删除并创建新发布证书
</el-button>
</el-tooltip>
<el-button v-if="isedit && editdeveloperinfo.is_actived" type="danger" size="small" <el-button v-if="isedit && editdeveloperinfo.is_actived" type="danger" size="small"
@click="activedeveloperFun(editdeveloperinfo,'checkauth')">开发者账户激活检测 @click="activedeveloperFun(editdeveloperinfo,'checkauth')">开发者账户激活检测
</el-button> </el-button>
@ -227,8 +265,7 @@
<el-input v-model="editdeveloperinfo.description"/> <el-input v-model="editdeveloperinfo.description"/>
</el-form-item> </el-form-item>
<div style=""> <div style="">
<el-button @click="updateorcreate">添加</el-button> <el-button @click="updateorcreate" type="primary" plain>保存信息并添加</el-button>
<el-button @click="canceledit">取消</el-button>
</div> </div>
</el-form> </el-form>
@ -409,13 +446,14 @@
<script> <script>
import {iosdeveloper, iosdevices, iosdevicesudid} from "@/restful"; import {iosdeveloper, iosdevices, iosdevicesudid, developercert} from "@/restful";
import {getUserInfoFun, removeAaary} from "@/utils"; import {getUserInfoFun, removeAaary} from "@/utils";
export default { export default {
name: "FirSuperSignBase", name: "FirSuperSignBase",
data() { data() {
return { return {
fileList: [],
app_developer_lists: [], app_developer_lists: [],
app_devices_lists: [], app_devices_lists: [],
app_udid_lists: [], app_udid_lists: [],
@ -424,6 +462,7 @@
Bundleidsearch: "", Bundleidsearch: "",
appidseach: "", appidseach: "",
dialogaddDeveloperVisible: false, dialogaddDeveloperVisible: false,
importcertDeveloperVisible: false,
title: "", title: "",
editdeveloperinfo: {auth_type: 0, usable_number: 100}, editdeveloperinfo: {auth_type: 0, usable_number: 100},
isedit: false, isedit: false,
@ -434,10 +473,53 @@
apple_auth_list: [], apple_auth_list: [],
apple_auth_type: 0, apple_auth_type: 0,
loadingfun: {}, loadingfun: {},
loading: false loading: false,
import_cert_info: {cert_pwd: '', cert_content: ''}
}
}, watch: {
'dialogaddDeveloperVisible': function () {
if (this.dialogaddDeveloperVisible === false) {
this.canceledit();
}
} }
}, },
methods: { methods: {
beforeAvatarUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 1;
if (file.type === 'application/x-pkcs12') {
if (isLt2M) {
this.import_cert_info.cert_content = file;
let reader = new FileReader();
reader.onload = evt => {
this.import_cert_info.cert_content = evt.target.result;
developercert(data => {
if (data.code === 1000) {
this.$message.success("证书导入成功");
this.importcertDeveloperVisible = false
} else {
this.$message.error("证书导入失败 " + data.msg)
}
}, {
methods: 'POST',
data: {
issuer_id: this.editdeveloperinfo.issuer_id,
cert_pwd: this.import_cert_info.cert_pwd,
cert_content: this.import_cert_info.cert_content
}
})
};
reader.readAsDataURL(file);
return false;
} else {
this.$message.error('上传大小有误');
}
} else {
this.$message.error('上传文件格式不正确!');
}
return false;
},
developer_usedColor(percentage) { developer_usedColor(percentage) {
if (percentage < 20) { if (percentage < 20) {
return '#6f7ad3'; return '#6f7ad3';
@ -484,6 +566,17 @@
"data": {"issuer_id": this.editdeveloperinfo.issuer_id, "act": "ioscert"} "data": {"issuer_id": this.editdeveloperinfo.issuer_id, "act": "ioscert"}
}); });
}, },
exportcert() {
// eslint-disable-next-line no-unused-vars
developercert(data => {
}, {methods: 'FILE', data: {issuer_id: this.editdeveloperinfo.issuer_id}})
},
isorenewcert() {
this.iosdeveloperFun({
"methods": "PUT",
"data": {"issuer_id": this.editdeveloperinfo.issuer_id, "act": "renewcert"}
});
},
activedeveloperFun(developer, act) { activedeveloperFun(developer, act) {
this.iosdeveloperFun({"methods": "PUT", "data": {"issuer_id": developer.issuer_id, "act": act}}); this.iosdeveloperFun({"methods": "PUT", "data": {"issuer_id": developer.issuer_id, "act": act}});
}, },

@ -14,6 +14,36 @@ const DOMAIN = process.env.base_env.baseUrl;
const APIPATH = '/api/v1/fir/server'; const APIPATH = '/api/v1/fir/server';
let USERSEVER = DOMAIN + APIPATH; let USERSEVER = DOMAIN + APIPATH;
export function convertRes2Blob(response) {
// 提取文件名
const fileName = response.headers['content-disposition'].match(
/filename=(.*)/
)[1];
// 将二进制流转为blob
const blob = new Blob([response.data], {type: 'application/octet-stream'});
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// 兼容IE,window.navigator.msSaveBlob:以本地方式保存文件
window.navigator.msSaveBlob(blob, decodeURI(fileName))
} else {
// 创建新的URL并指向File对象或者Blob对象的地址
const blobURL = window.URL.createObjectURL(blob);
// 创建a标签,用于跳转至下载链接
const tempLink = document.createElement('a');
tempLink.style.display = 'none';
tempLink.href = blobURL;
tempLink.setAttribute('download', decodeURI(fileName));
// 兼容:某些浏览器不支持HTML5的download属性
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
// 挂载a标签
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
// 释放blob URL地址
window.URL.revokeObjectURL(blobURL)
}
}
export function set_auth_token() { export function set_auth_token() {
Axios.interceptors.request.use(function (config) { Axios.interceptors.request.use(function (config) {
@ -146,6 +176,16 @@ function getData(methods, url, params = {}, callBack, load, isCode = false) {
ErrorMsg(error); ErrorMsg(error);
callBack({"code": -1}); callBack({"code": -1});
}); });
} else if (methods === 'FILE') {
Axios
.get(url, {params: params, responseType: 'blob'})
.then(function (response) {
convertRes2Blob(response)
})
.catch(function (error) {
ErrorMsg(error);
callBack({"code": -1});
});
} else { } else {
Axios Axios
.get(url, {params: params}) .get(url, {params: params})
@ -570,3 +610,19 @@ export function gettask(callBack, params, load = true) {
true true
); );
} }
/**签名证书 */
export function developercert(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/supersign/cert',
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}

@ -24,7 +24,7 @@ from api.views.uploads import AppAnalyseView, UploadView
from api.views.storage import StorageView from api.views.storage import StorageView
from api.views.receiveudids import IosUDIDView, TaskView from api.views.receiveudids import IosUDIDView, TaskView
from api.views.order import PriceView, OrderView, PaySuccess from api.views.order import PriceView, OrderView, PaySuccess
from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView from api.views.supersign import DeveloperView, SuperSignUsedView, AppUDIDUsedView, SuperSignCertView
from api.views.domain import DomainCnameView from api.views.domain import DomainCnameView
# router=DefaultRouter() # router=DefaultRouter()
@ -52,6 +52,7 @@ urlpatterns = [
re_path("^supersign/developer$", DeveloperView.as_view()), re_path("^supersign/developer$", DeveloperView.as_view()),
re_path("^supersign/devices$", SuperSignUsedView.as_view()), re_path("^supersign/devices$", SuperSignUsedView.as_view()),
re_path("^supersign/udid$", AppUDIDUsedView.as_view()), re_path("^supersign/udid$", AppUDIDUsedView.as_view()),
re_path("^supersign/cert$", SuperSignCertView.as_view()),
re_path("^package_prices$", PriceView.as_view()), re_path("^package_prices$", PriceView.as_view()),
re_path("^orders$", OrderView.as_view()), re_path("^orders$", OrderView.as_view()),
re_path("^certification$", CertificationView.as_view()), re_path("^certification$", CertificationView.as_view()),

@ -3,19 +3,20 @@
# project: 4月 # project: 4月
# author: liuyu # author: liuyu
# date: 2020/4/24 # date: 2020/4/24
import OpenSSL import datetime
from api.utils.app.shellcmds import shell_command, use_user_pass from api.utils.app.shellcmds import shell_command, use_user_pass
from api.utils.baseutils import get_format_time, format_apple_date
from fir_ser.settings import SUPER_SIGN_ROOT from fir_ser.settings import SUPER_SIGN_ROOT
import os import os
from api.utils.app.randomstrings import make_app_uuid from api.utils.app.randomstrings import make_app_uuid
import logging import logging
from api.utils.apple.appleapiv3 import AppStoreConnectApi from api.utils.apple.appleapiv3 import AppStoreConnectApi
import base64
from OpenSSL.crypto import (load_pkcs12, dump_certificate_request, dump_privatekey, PKey, TYPE_RSA, X509Req,
dump_certificate, load_privatekey, load_certificate, PKCS12, FILETYPE_PEM, FILETYPE_ASN1)
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
import base64
from OpenSSL.SSL import FILETYPE_PEM
from OpenSSL.crypto import (dump_certificate_request, dump_privatekey, PKey, TYPE_RSA, X509Req, dump_certificate)
def exec_shell(cmd, remote=False, timeout=None): def exec_shell(cmd, remote=False, timeout=None):
@ -41,9 +42,10 @@ def exec_shell(cmd, remote=False, timeout=None):
class ResignApp(object): class ResignApp(object):
def __init__(self, my_local_key, app_dev_pem): def __init__(self, my_local_key, app_dev_pem, app_dev_p12):
self.my_local_key = my_local_key self.my_local_key = my_local_key
self.app_dev_pem = app_dev_pem self.app_dev_pem = app_dev_pem
self.app_dev_p12 = app_dev_p12
self.cmd = "zsign -c '%s' -k '%s' " % (self.app_dev_pem, self.my_local_key) self.cmd = "zsign -c '%s' -k '%s' " % (self.app_dev_pem, self.my_local_key)
@staticmethod @staticmethod
@ -53,6 +55,51 @@ 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 make_p12_from_cert(self, password):
result = {}
try:
certificate = load_certificate(FILETYPE_PEM, open(self.app_dev_pem, 'rb').read())
private_key = load_privatekey(FILETYPE_PEM, open(self.my_local_key, 'rb').read())
p12 = PKCS12()
p12.set_certificate(certificate)
p12.set_privatekey(private_key)
with open(self.app_dev_p12, 'wb+') as f:
f.write(p12.export(password))
return True, p12.get_friendlyname()
except Exception as e:
result["err_info"] = e
return False, result
def make_cert_from_p12(self, password, p12_content=None):
result = {}
try:
if p12_content:
with open(self.app_dev_p12 + '.bak', 'wb+') as f:
f.write(base64.b64decode(p12_content.split('data:application/x-pkcs12;base64,')[1]))
if password:
with open(self.app_dev_p12 + '.pwd.bak', 'w') as f:
f.write(password)
p12 = load_pkcs12(open(self.app_dev_p12, 'rb').read(), password)
cert = p12.get_certificate()
if cert.has_expired():
result["err_info"] = '证书已经过期'
return False, result
with open(self.my_local_key + '.bak', 'wb+') as f:
f.write(dump_privatekey(FILETYPE_PEM, p12.get_privatekey()))
with open(self.app_dev_pem + '.bak', 'wb+') as f:
f.write(dump_certificate(FILETYPE_PEM, cert))
for file in [self.app_dev_p12, self.app_dev_p12 + '.pwd', self.my_local_key, self.app_dev_pem]:
if os.path.exists(file):
os.rename(file, file + '.' + get_format_time() + '.bak')
os.rename(file + '.bak', file)
return True, cert.get_version()
except Exception as e:
for file in [self.app_dev_p12, self.app_dev_p12 + '.pwd', self.my_local_key, self.app_dev_pem]:
if os.path.exists(file + '.bak'):
os.remove(file + '.bak')
result["err_info"] = e
return False, result
def sign(self, new_profile, org_ipa, new_ipa, info_plist_properties=None): def sign(self, new_profile, org_ipa, new_ipa, info_plist_properties=None):
if info_plist_properties is None: if info_plist_properties is None:
info_plist_properties = {} info_plist_properties = {}
@ -115,7 +162,7 @@ class AppDeveloperApiV2(object):
return csr_content return csr_content
def make_pem(self, cer_content, pem_path): def make_pem(self, cer_content, pem_path):
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1, cer_content) cert = load_certificate(FILETYPE_ASN1, cer_content)
with open(pem_path, 'wb+') as f: with open(pem_path, 'wb+') as f:
f.write(dump_certificate(FILETYPE_PEM, cert)) f.write(dump_certificate(FILETYPE_PEM, cert))
@ -140,6 +187,35 @@ class AppDeveloperApiV2(object):
result['return_info'] = "%s" % e result['return_info'] = "%s" % e
return False, result return False, result
def revoke_cert(self, cert_id):
result = {}
try:
apple_obj = AppStoreConnectApi(self.issuer_id, self.private_key_id, self.p8key)
if apple_obj.revoke_certificate(cert_id):
logger.info("ios developer cert %s revoke result:%s" % cert_id)
return True, result
except Exception as e:
logger.error("ios developer cert %s revoke Failed Exception:%s" % (cert_id, e))
result['return_info'] = "%s" % e
return False, result
def auto_set_certid_by_p12(self, app_dev_pem):
result = {}
try:
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()
for cert_obj in certificates:
f_date = format_apple_date(cert_obj.expirationDate)
if not_after.timestamp() == f_date.timestamp():
return True, cert_obj
return False, result
except Exception as e:
logger.error("ios developer cert %s auto get Failed Exception:%s" % (111, e))
result['return_info'] = "%s" % e
return False, result
def get_profile(self, app_obj, udid_info, 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 = {}

@ -6,6 +6,8 @@
import uuid, xmltodict, os, re, logging, time import uuid, xmltodict, os, re, logging, time
from django.utils import timezone
import zipfile
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from fir_ser.settings import SUPER_SIGN_ROOT, MEDIA_ROOT, SERVER_DOMAIN, MOBILECONFIG_SIGN_SSL, MSGTEMPLATE from fir_ser.settings import SUPER_SIGN_ROOT, MEDIA_ROOT, SERVER_DOMAIN, MOBILECONFIG_SIGN_SSL, MSGTEMPLATE
from api.utils.app.iossignapi import ResignApp, AppDeveloperApiV2 from api.utils.app.iossignapi import ResignApp, AppDeveloperApiV2
@ -17,7 +19,7 @@ from api.utils.storage.caches import del_cache_response_by_short, send_msg_over_
from api.utils.utils import delete_app_to_dev_and_file, send_ios_developer_active_status, delete_local_files, \ from api.utils.utils import delete_app_to_dev_and_file, send_ios_developer_active_status, delete_local_files, \
download_files_form_oss, get_developer_udided download_files_form_oss, get_developer_udided
from api.utils.baseutils import file_format_path, delete_app_profile_file, get_profile_full_path, get_user_domain_name, \ from api.utils.baseutils import file_format_path, delete_app_profile_file, get_profile_full_path, get_user_domain_name, \
get_user_default_domain_name, get_min_default_domain_cname_obj, format_apple_date get_user_default_domain_name, get_min_default_domain_cname_obj, format_apple_date, get_format_time
from api.utils.storage.storage import Storage from api.utils.storage.storage import Storage
from django.core.cache import cache from django.core.cache import cache
@ -356,12 +358,31 @@ class IosUtils(object):
return True, result return True, result
@staticmethod @staticmethod
def exec_sign(user_obj, app_obj, developer_obj, random_file_name, release_obj): def zip_cert(user_obj, developer_obj):
auth = get_auth_form_developer(developer_obj)
file_format_path_name = file_format_path(user_obj, auth)
os.chdir(os.path.dirname(file_format_path_name))
zip_file_path = file_format_path_name + '.zip'
zipf = zipfile.ZipFile(zip_file_path, mode='w', compression=zipfile.ZIP_DEFLATED)
for file in os.listdir(os.path.dirname(file_format_path_name)):
if os.path.isfile(file) and file.startswith(os.path.basename(file_format_path_name)) and \
file.split('.')[-1] not in ['zip', 'bak']:
zipf.write(file)
zipf.close()
return zip_file_path
@staticmethod
def get_resign_obj(user_obj, developer_obj):
auth = get_auth_form_developer(developer_obj) auth = get_auth_form_developer(developer_obj)
file_format_path_name = file_format_path(user_obj, auth) 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"
resign_app_obj = ResignApp(my_local_key, app_dev_pem) app_dev_p12 = file_format_path_name + ".p12"
return ResignApp(my_local_key, app_dev_pem, app_dev_p12)
@staticmethod
def exec_sign(user_obj, app_obj, developer_obj, random_file_name, release_obj):
resign_app_obj = IosUtils.get_resign_obj(user_obj, developer_obj)
org_file = os.path.join(MEDIA_ROOT, release_obj.release_id + ".ipa") org_file = os.path.join(MEDIA_ROOT, release_obj.release_id + ".ipa")
check_org_file(user_obj, org_file) check_org_file(user_obj, org_file)
new_file = os.path.join(MEDIA_ROOT, random_file_name + ".ipa") new_file = os.path.join(MEDIA_ROOT, random_file_name + ".ipa")
@ -618,34 +639,44 @@ class IosUtils(object):
delete_app_profile_file(developer_obj, app_obj) delete_app_profile_file(developer_obj, app_obj)
@staticmethod @staticmethod
def clean_app_by_developer_obj(app_obj, developer_obj): def clean_app_by_developer_obj(app_obj, developer_obj, cert_id=None):
auth = get_auth_form_developer(developer_obj) auth = get_auth_form_developer(developer_obj)
DeveloperAppID.objects.filter(developerid=developer_obj, app_id=app_obj).delete() DeveloperAppID.objects.filter(developerid=developer_obj, app_id=app_obj).delete()
app_api_obj = get_api_obj(auth) app_api_obj = get_api_obj(auth)
app_api_obj.del_profile(app_obj.app_id) app_api_obj.del_profile(app_obj.app_id)
if not cert_id:
app_api_obj2 = get_api_obj(auth) app_api_obj2 = get_api_obj(auth)
app_api_obj2.del_app(app_obj.bundle_id, app_obj.app_id) app_api_obj2.del_app(app_obj.bundle_id, app_obj.app_id)
@staticmethod @staticmethod
def clean_developer(developer_obj, user_obj): def clean_developer(developer_obj, user_obj, cert_id=None):
''' '''
根据消耗记录 删除该苹果账户下所有信息 根据消耗记录 删除该苹果账户下所有信息
:param user_obj:
:param cert_id:
:param developer_obj: :param developer_obj:
:return: :return:
''' '''
for APPToDeveloper_obj in APPToDeveloper.objects.filter(developerid=developer_obj): for APPToDeveloper_obj in APPToDeveloper.objects.filter(developerid=developer_obj):
app_obj = APPToDeveloper_obj.app_id app_obj = APPToDeveloper_obj.app_id
IosUtils.clean_app_by_developer_obj(app_obj, developer_obj) IosUtils.clean_app_by_developer_obj(app_obj, developer_obj, cert_id)
delete_app_to_dev_and_file(developer_obj, app_obj.id) delete_app_to_dev_and_file(developer_obj, app_obj.id)
if not cert_id:
IosUtils.clean_udid_by_app_obj(app_obj, developer_obj) IosUtils.clean_udid_by_app_obj(app_obj, developer_obj)
full_path = file_format_path(user_obj, get_auth_form_developer(developer_obj)) full_path = file_format_path(user_obj, get_auth_form_developer(developer_obj))
try: try:
for root, dirs, files in os.walk(full_path, topdown=False): # move dirs replace delete
for name in files: new_full_path_dir = os.path.dirname(full_path)
os.remove(os.path.join(root, name)) new_full_path_name = os.path.basename(full_path)
for name in dirs: new_full_path = os.path.join(new_full_path_dir,
os.rmdir(os.path.join(root, name)) '%s_%s_%s' % ('remove', new_full_path_name, get_format_time()))
os.rmdir(full_path) os.rename(full_path, new_full_path)
# 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: except Exception as e:
logger.error("clean_developer developer_obj:%s user_obj:%s delete file failed Exception:%s" % ( logger.error("clean_developer developer_obj:%s user_obj:%s delete file failed Exception:%s" % (
developer_obj, user_obj, e)) developer_obj, user_obj, e))
@ -683,6 +714,30 @@ class IosUtils(object):
certid=cert_id, cert_expire_time=format_apple_date(result.expirationDate)) certid=cert_id, cert_expire_time=format_apple_date(result.expirationDate))
return status, result return status, result
@staticmethod
def revoke_developer_cert(developer_obj, user_obj):
auth = get_auth_form_developer(developer_obj)
app_api_obj = get_api_obj(auth)
status, result = app_api_obj.revoke_cert(developer_obj.certid)
if status:
AppIOSDeveloperInfo.objects.filter(user_id=user_obj, issuer_id=auth.get("issuer_id")).update(
is_actived=True,
certid=None, cert_expire_time=None)
return status, result
@staticmethod
def auto_get_certid_by_p12(developer_obj, user_obj):
auth = get_auth_form_developer(developer_obj)
app_api_obj = get_api_obj(auth)
file_format_path_name = file_format_path(user_obj, auth)
app_dev_pem = file_format_path_name + ".pem"
status, result = app_api_obj.auto_set_certid_by_p12(app_dev_pem)
if status:
AppIOSDeveloperInfo.objects.filter(user_id=user_obj, issuer_id=auth.get("issuer_id")).update(
is_actived=True,
certid=result.id, cert_expire_time=format_apple_date(result.expirationDate))
return status, result
@staticmethod @staticmethod
def get_device_from_developer(developer_obj, user_obj): def get_device_from_developer(developer_obj, user_obj):
auth = get_auth_form_developer(developer_obj) auth = get_auth_form_developer(developer_obj)

@ -874,3 +874,9 @@ class AppStoreConnectApi(DevicesAPI, BundleIDsAPI, BundleIDsCapabilityAPI, Profi
if req.status_code == 201: if req.status_code == 201:
return self.__certificates_store(req, 201) return self.__certificates_store(req, 201)
raise KeyError(req.text) raise KeyError(req.text)
def revoke_certificate(self, certificate_id):
req = super().revoke_certificate(certificate_id)
if req.status_code == 204:
return True
return False

@ -151,3 +151,10 @@ def format_apple_date(s_date):
if not timezone.is_naive(f_date): if not timezone.is_naive(f_date):
f_date = timezone.make_naive(f_date, timezone.utc) f_date = timezone.make_naive(f_date, timezone.utc)
return f_date return f_date
def get_format_time():
now = timezone.now()
if not timezone.is_naive(now):
now = timezone.make_naive(now, timezone.utc)
return now.strftime('%Y-%m-%d_%H:%M:%S')

@ -24,24 +24,24 @@ logger = logging.getLogger(__name__)
def delete_app_to_dev_and_file(developer_obj, app_id): def delete_app_to_dev_and_file(developer_obj, app_id):
APPToDeveloper_obj = APPToDeveloper.objects.filter(developerid=developer_obj, app_id_id=app_id) app_to_developer_obj = APPToDeveloper.objects.filter(developerid=developer_obj, app_id_id=app_id)
if APPToDeveloper_obj: if app_to_developer_obj:
binary_file = APPToDeveloper_obj.first().binary_file + ".ipa" binary_file = app_to_developer_obj.first().binary_file + ".ipa"
delete_local_files(binary_file) delete_local_files(binary_file)
storage = Storage(developer_obj.user_id) storage = Storage(developer_obj.user_id)
storage.delete_file(binary_file) storage.delete_file(binary_file)
APPToDeveloper_obj.delete() app_to_developer_obj.delete()
def get_developer_udided(developer_obj): def get_developer_udided(developer_obj):
SuperSignUsed_obj = APPSuperSignUsedInfo.objects.filter(developerid=developer_obj) super_sing_used_obj = APPSuperSignUsedInfo.objects.filter(developerid=developer_obj)
UDIDsyncDeveloper_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj) udid_sync_developer_obj = UDIDsyncDeveloper.objects.filter(developerid=developer_obj)
develoer_udid_lists = [] develoer_udid_lists = []
supersign_udid_lists = [] supersign_udid_lists = []
if UDIDsyncDeveloper_obj: if udid_sync_developer_obj:
develoer_udid_lists = list(UDIDsyncDeveloper_obj.values_list("udid")) develoer_udid_lists = list(udid_sync_developer_obj.values_list("udid"))
if SuperSignUsed_obj: if super_sing_used_obj:
supersign_udid_lists = list(SuperSignUsed_obj.values_list("udid__udid")) supersign_udid_lists = list(super_sing_used_obj.values_list("udid__udid"))
return len(set(develoer_udid_lists) - set(supersign_udid_lists)), len(develoer_udid_lists) return len(set(develoer_udid_lists) - set(supersign_udid_lists)), len(develoer_udid_lists)

@ -3,15 +3,18 @@
# project: 3月 # project: 3月
# author: liuyu # author: liuyu
# date: 2020/3/4 # date: 2020/3/4
from django.http.response import FileResponse
from rest_framework.views import APIView from rest_framework.views import APIView
from api.utils.app.iossignapi import ResignApp
from api.utils.baseutils import file_format_path
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication, SuperSignPermission from api.utils.auth import ExpiringTokenAuthentication, SuperSignPermission
from rest_framework.response import Response from rest_framework.response import Response
from api.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID from api.models import AppIOSDeveloperInfo, APPSuperSignUsedInfo, AppUDID
from api.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer from api.utils.serializer import DeveloperSerializer, SuperSignUsedSerializer, DeviceUDIDSerializer
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import PageNumberPagination
from api.utils.app.supersignutils import IosUtils from api.utils.app.supersignutils import IosUtils, get_auth_form_developer
from api.utils.utils import get_developer_devices, get_choices_dict from api.utils.utils import get_developer_devices, get_choices_dict
import logging import logging
@ -84,6 +87,23 @@ class DeveloperView(APIView):
res.code = 1008 res.code = 1008
res.msg = result.get("err_info") res.msg = result.get("err_info")
return Response(res.dict) return Response(res.dict)
elif act == "renewcert":
if developer_obj.certid:
# clean developer somethings. remove profile and revoke cert
IosUtils.clean_developer(developer_obj, request.user)
status, result = IosUtils.revoke_developer_cert(developer_obj, request.user)
if status:
status, result = IosUtils.create_developer_cert(developer_obj, request.user)
if status:
IosUtils.get_device_from_developer(developer_obj, request.user)
else:
res.code = 1008
res.msg = result.get("err_info")
return Response(res.dict)
else:
res.code = 1008
res.msg = result.get("err_info")
return Response(res.dict)
elif act == "syncdevice": elif act == "syncdevice":
status, result = IosUtils.get_device_from_developer(developer_obj, request.user) status, result = IosUtils.get_device_from_developer(developer_obj, request.user)
if not status: if not status:
@ -234,3 +254,40 @@ class AppUDIDUsedView(APIView):
IosUtils.disable_udid(app_udid_obj.first(), app_id) IosUtils.disable_udid(app_udid_obj.first(), app_id)
app_udid_obj.delete() app_udid_obj.delete()
return Response(res.dict) return Response(res.dict)
class SuperSignCertView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [SuperSignPermission, ]
def get(self, request):
issuer_id = request.query_params.get('issuer_id', None)
if issuer_id:
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user, issuer_id=issuer_id).first()
if developer_obj:
zip_file_path = IosUtils.zip_cert(request.user, developer_obj)
response = FileResponse(open(zip_file_path, 'rb'))
response['Content-Type'] = "application/octet-stream"
response["Access-Control-Expose-Headers"] = "Content-Disposition"
response['Content-Disposition'] = 'attachment; filename=' + developer_obj.certid + '.zip'
return response
res = BaseResponse()
return Response(res.dict)
def post(self, request):
res = BaseResponse()
issuer_id = request.data.get('issuer_id', None)
if issuer_id:
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=request.user, issuer_id=issuer_id).first()
if developer_obj:
resign_app_obj = IosUtils.get_resign_obj(request.user, developer_obj)
status, result = resign_app_obj.make_cert_from_p12(request.data.get('cert_pwd', ''),
request.data.get('cert_content', None))
if not status:
res.code = 1002
res.msg = str(result['err_info'])
status, result = IosUtils.auto_get_certid_by_p12(developer_obj, request.user)
if not status:
res.code = 1003
res.msg = str('证书未在开发者账户找到,请检查推送证书是否属于该开发者')
return Response(res.dict)

Loading…
Cancel
Save