实现超级签名方法

super_signature
nineven 5 years ago
parent 8de6e4a87d
commit 789b9a0c58
  1. 55
      fir_client/src/components/FirAppInfossecurity.vue
  2. 19
      fir_client/src/components/FirDownload.vue
  3. 3
      fir_ser/api/admin.py
  4. 18
      fir_ser/api/migrations/0027_apps_issupersign.py
  5. 31
      fir_ser/api/migrations/0028_appudid.py
  6. 38
      fir_ser/api/migrations/0029_auto_20200416_0915.py
  7. 18
      fir_ser/api/migrations/0030_appudid_updated_time.py
  8. 18
      fir_ser/api/migrations/0031_appudid_is_signed.py
  9. 49
      fir_ser/api/migrations/0032_appiosdeveloperinfo_appsupersignusedinfo.py
  10. 23
      fir_ser/api/migrations/0033_auto_20200425_1601.py
  11. 18
      fir_ser/api/migrations/0034_appiosdeveloperinfo_certid.py
  12. 65
      fir_ser/api/models.py
  13. 2
      fir_ser/api/urls.py
  14. 51
      fir_ser/api/utils/TokenManager.py
  15. 2
      fir_ser/api/utils/app/apputils.py
  16. 96
      fir_ser/api/utils/app/iossignapi.py
  17. 127
      fir_ser/api/utils/app/shellcmds.py
  18. 163
      fir_ser/api/utils/app/supersignutils.py
  19. 2
      fir_ser/api/utils/serializer.py
  20. 18
      fir_ser/api/utils/storage/caches.py
  21. 3
      fir_ser/api/views.py
  22. 3
      fir_ser/api/views/apps.py
  23. 64
      fir_ser/api/views/download.py
  24. 7
      fir_ser/fir_ser/settings.py
  25. 157
      fir_ser/supersign/scripts/apple_api.rb

@ -42,6 +42,22 @@
</el-form-item> </el-form-item>
<el-form-item v-if="currentapp.type === 1" label-width="160px" 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-form-item>
</el-form> </el-form>
</div> </div>
@ -57,10 +73,12 @@
data() { data() {
return { return {
currentapp: {}, currentapp: {},
downtip:{}, downtip:{'msg':''},
supersign:{'msg':''},
passwordtip:{'msg':''}, passwordtip:{'msg':''},
passwordflag:false, passwordflag:false,
showdownloadflag:false, showdownloadflag:false,
showsupersignflag:false,
} }
}, },
methods: { methods: {
@ -96,9 +114,20 @@
} }
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;
},
setbuttondefault(currentapp){ setbuttondefault(currentapp){
this.setbuttondefaltpass(currentapp); this.setbuttondefaltpass(currentapp);
this.setbuttondefaltshow(currentapp); this.setbuttondefaltshow(currentapp);
this.setbuttonsignshow(currentapp);
}, },
passwordswitch(state){ passwordswitch(state){
this.passwordflag=false; this.passwordflag=false;
@ -158,18 +187,35 @@
"isshow": 1, "isshow": 1,
}); });
this.currentapp.isshow=1; this.currentapp.isshow=1;
}else {
this.downtip.msg='下载页对所有人可见';
} }
this.downtip.msg='下载页对所有人可见';
}else { }else {
if(this.showdownloadflag){ if(this.showdownloadflag){
this.saveappinfo({ this.saveappinfo({
"isshow": 0, "isshow": 0,
}); });
this.currentapp.isshow=0; this.currentapp.isshow=0;
}else { }
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){
@ -194,6 +240,7 @@
this.currentapp = this.$store.state.currentapp; this.currentapp = this.$store.state.currentapp;
this.passwordflag=false; this.passwordflag=false;
this.showdownloadflag=false; this.showdownloadflag=false;
this.showsupersignflag=false;
this.setbuttondefault(this.currentapp); this.setbuttondefault(this.currentapp);
} }
}, },

@ -167,6 +167,7 @@
dchoice:false, dchoice:false,
downloadurl:"", downloadurl:"",
isdownload:false, isdownload:false,
udid:"",
} }
}, methods: { }, methods: {
download() { download() {
@ -176,9 +177,16 @@
if(res.code === 1000){ if(res.code === 1000){
if(this.currentappinfo.type === 1){ if(this.currentappinfo.type === 1){
let download_url = res.data.download_url; if(this.currentappinfo.issupersign && this.udid !== this.$route.query.udid){
download_url = download_url.replace('http://localhost/download',getplisturl()); if(this.agent !== ''){
this.downloadurl="itms-services://?action=download-manifest&url="+encodeURIComponent(download_url); let download_url = res.data.download_url;
this.downloadurl = download_url.replace('http://localhost/download',getplisturl());
}
}else {
let download_url = res.data.download_url;
download_url = download_url.replace('http://localhost/download',getplisturl());
this.downloadurl="itms-services://?action=download-manifest&url="+encodeURIComponent(download_url);
}
}else{ }else{
if(this.agent !== ''){ if(this.agent !== ''){
this.downloadurl = res.data.download_url; this.downloadurl = res.data.download_url;
@ -201,6 +209,7 @@
'short': this.currentappinfo.short, 'short': this.currentappinfo.short,
'release_id': this.mcurrentappinfo.release_id, 'release_id': this.mcurrentappinfo.release_id,
'password':this.password, 'password':this.password,
'udid':this.udid
}, },
'app_id': this.currentappinfo.app_id 'app_id': this.currentappinfo.app_id
}) })
@ -218,7 +227,11 @@
if(this.$route.query.release_id){ if(this.$route.query.release_id){
params["release_id"]=this.$route.query.release_id params["release_id"]=this.$route.query.release_id
} }
if(this.$route.query.udid){
params["udid"]=this.$route.query.udid
}
getShortAppinfo(data => { getShortAppinfo(data => {
this.udid = data.udid;
if (data.code === 1000) { if (data.code === 1000) {
if(!data.data.master_release.release_id){ if(!data.data.master_release.release_id){
this.$message({ this.$message({

@ -10,4 +10,7 @@ admin.site.register(AppReleaseInfo)
admin.site.register(Token) admin.site.register(Token)
admin.site.register(VerifyName) admin.site.register(VerifyName)
admin.site.register(AppStorage) admin.site.register(AppStorage)
admin.site.register(AppUDID)
admin.site.register(AppIOSDeveloperInfo)
admin.site.register(APPSuperSignUsedInfo)

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-04-15 19:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0026_auto_20200414_1505'),
]
operations = [
migrations.AddField(
model_name='apps',
name='issupersign',
field=models.BigIntegerField(default=False, verbose_name='是否超级签名包'),
),
]

@ -0,0 +1,31 @@
# Generated by Django 3.0.3 on 2020-04-16 08:33
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('api', '0027_apps_issupersign'),
]
operations = [
migrations.CreateModel(
name='AppUDID',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('udid', models.CharField(db_index=True, max_length=64, unique=True, verbose_name='udid唯一标识')),
('product', models.CharField(db_index=True, max_length=64, verbose_name='产品')),
('serial', models.CharField(db_index=True, max_length=64, verbose_name='序列号')),
('version', models.CharField(db_index=True, max_length=64, verbose_name='型号')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('app_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
],
options={
'verbose_name': '应用详情',
'verbose_name_plural': '应用详情',
'unique_together': {('app_id', 'udid')},
},
),
]

@ -0,0 +1,38 @@
# Generated by Django 3.0.3 on 2020-04-16 09:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0028_appudid'),
]
operations = [
migrations.AddField(
model_name='appudid',
name='iccid',
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='型号'),
),
migrations.AddField(
model_name='appudid',
name='imei',
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='型号'),
),
migrations.AlterField(
model_name='appudid',
name='product',
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='产品'),
),
migrations.AlterField(
model_name='appudid',
name='serial',
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='序列号'),
),
migrations.AlterField(
model_name='appudid',
name='version',
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='型号'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-04-16 10:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0029_auto_20200416_0915'),
]
operations = [
migrations.AddField(
model_name='appudid',
name='updated_time',
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-04-23 17:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0030_appudid_updated_time'),
]
operations = [
migrations.AddField(
model_name='appudid',
name='is_signed',
field=models.BooleanField(default=False, verbose_name='是否完成签名打包'),
),
]

@ -0,0 +1,49 @@
# Generated by Django 3.0.3 on 2020-04-25 15:30
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('api', '0031_appudid_is_signed'),
]
operations = [
migrations.CreateModel(
name='APPSuperSignUsedInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('app_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Apps', verbose_name='属于哪个APP')),
('udid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.AppUDID', verbose_name='所消耗的udid')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户ID')),
],
options={
'verbose_name': '设备使用统计',
'verbose_name_plural': '设备使用统计',
},
),
migrations.CreateModel(
name='AppIOSDeveloperInfo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64, unique=True, verbose_name='存储名字')),
('email', models.EmailField(blank=True, max_length=64, null=True, unique=True, verbose_name='email address')),
('password', models.CharField(max_length=64)),
('is_actived', models.BooleanField(default=False, verbose_name='是否已经激活')),
('usable_number', models.IntegerField(default=100, verbose_name='可使用设备数')),
('use_number', models.IntegerField(default=0, verbose_name='已消耗设备数')),
('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户ID')),
],
options={
'verbose_name': '苹果开发者账户',
'verbose_name_plural': '苹果开发者账户',
},
),
]

@ -0,0 +1,23 @@
# Generated by Django 3.0.3 on 2020-04-25 16:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0032_appiosdeveloperinfo_appsupersignusedinfo'),
]
operations = [
migrations.AddField(
model_name='appudid',
name='binary_file',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='签名包名称'),
),
migrations.AlterField(
model_name='appiosdeveloperinfo',
name='name',
field=models.CharField(max_length=64, unique=True, verbose_name='开发者标识'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-04-25 16:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0033_auto_20200425_1601'),
]
operations = [
migrations.AddField(
model_name='appiosdeveloperinfo',
name='certid',
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='超级签名自动创建证书ID'),
),
]

@ -3,7 +3,7 @@ from django.db import models
# Create your models here. # Create your models here.
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from api.utils.app.randomstrings import make_random_uuid from api.utils.app.randomstrings import make_random_uuid,make_app_uuid
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
######################################## 用户表 ######################################## ######################################## 用户表 ########################################
@ -105,6 +105,7 @@ class Apps(models.Model):
count_hits = models.BigIntegerField(verbose_name="下载次数",default=0) count_hits = models.BigIntegerField(verbose_name="下载次数",default=0)
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)
description = models.TextField('描述', blank=True, null=True, default=None, ) description = models.TextField('描述', blank=True, null=True, default=None, )
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta: class Meta:
@ -164,3 +165,65 @@ class AppStorage(models.Model):
def __str__(self): def __str__(self):
return "%s %s" % (self.user_id.get_username(),self.name) return "%s %s" % (self.user_id.get_username(),self.name)
class AppUDID(models.Model):
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="属于哪个APP")
udid = models.CharField(max_length=64, unique=True,verbose_name="udid唯一标识",db_index=True)
product = models.CharField(max_length=64, verbose_name="产品", blank=True, null=True, )
serial = models.CharField(max_length=64, verbose_name="序列号", blank=True, null=True, )
version = models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
imei= models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
iccid= models.CharField(max_length=64, verbose_name="型号", blank=True, null=True, )
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
is_signed = models.BooleanField(verbose_name="是否完成签名打包",default=False)
binary_file = models.CharField(max_length=128,blank=True,verbose_name="签名包名称",null=True)
class Meta:
verbose_name = '应用详情'
verbose_name_plural = "应用详情"
unique_together = ('app_id', 'udid',)
def __str__(self):
return "%s-%s" % (self.app_id.name,self.udid)
class AppIOSDeveloperInfo(models.Model):
user_id = models.ForeignKey(to="UserInfo",verbose_name="用户ID",on_delete=models.CASCADE)
name = models.CharField(max_length=64,verbose_name="开发者标识",unique=True)
email = models.EmailField(
verbose_name='email address',
max_length=64,
unique=True,
blank=True,
null=True
)
password = models.CharField(max_length=64,)
is_actived = models.BooleanField(default=False,verbose_name="是否已经激活")
certid = models.CharField(max_length=64,blank=True,verbose_name="超级签名自动创建证书ID",null=True)
usable_number = models.IntegerField(verbose_name="可使用设备数",default=100)
use_number = models.IntegerField(verbose_name="已消耗设备数",default=0)
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '苹果开发者账户'
verbose_name_plural = "苹果开发者账户"
def __str__(self):
return "%s-%s" % (self.name,self.email)
class APPSuperSignUsedInfo(models.Model):
user_id = models.ForeignKey(to="UserInfo",verbose_name="用户ID",on_delete=models.CASCADE)
app_id = models.ForeignKey(to="Apps", on_delete=models.CASCADE, verbose_name="属于哪个APP")
udid = models.ForeignKey(to="AppUDID",on_delete=models.CASCADE, verbose_name="所消耗的udid")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
verbose_name = '设备使用统计'
verbose_name_plural = "设备使用统计"
def __str__(self):
return "%s-%s-%s" % (self.user_id,self.app_id,self.udid)

@ -23,6 +23,7 @@ from api.views.apps import AppsView,AppInfoView,AppReleaseinfoView
from api.views.download import ShortDownloadView from api.views.download import ShortDownloadView
from api.views.uploads import AppAnalyseView,UploadView from api.views.uploads import AppAnalyseView,UploadView
from api.views.storage import StorageView from api.views.storage import StorageView
from api.views.download import IosUDIDView
# router=DefaultRouter() # router=DefaultRouter()
@ -40,5 +41,6 @@ urlpatterns = [
re_path("^upload$",UploadView.as_view()), re_path("^upload$",UploadView.as_view()),
re_path("^userinfo",UserInfoView.as_view()), re_path("^userinfo",UserInfoView.as_view()),
re_path("^short/(?P<short>\w+)$", ShortDownloadView.as_view()), re_path("^short/(?P<short>\w+)$", ShortDownloadView.as_view()),
re_path("^udid/(?P<short>\w+)$", IosUDIDView.as_view()),
re_path("^analyse$", AppAnalyseView.as_view()), re_path("^analyse$", AppAnalyseView.as_view()),
] ]

@ -7,7 +7,6 @@ import uuid
import string import string
import random import random
import time import time
from threading import Thread
from django.core.cache import cache from django.core.cache import cache
from fir_ser.settings import CACHE_KEY_TEMPLATE from fir_ser.settings import CACHE_KEY_TEMPLATE
@ -48,56 +47,6 @@ class DownloadToken(object):
return False return False
class DownloadTokenLocal(object):
TokenLists = []
flag={'stat':1}
def __init__(self):
self.auto_clean_totken()
def make_token(self,release_id,time_limit=60):
random_str = uuid.uuid1().__str__().split("-")[0:-1]
user_ran_str = uuid.uuid5(uuid.NAMESPACE_DNS, release_id).__str__().split("-")
user_ran_str.extend(random_str)
new_str = "".join(user_ran_str)
self.TokenLists.append({new_str:{
"atime":time.time()+time_limit,
"data":release_id
}})
return new_str
def verify_token(self,token,release_id):
try:
for itoken in self.TokenLists:
if itoken.get(token,None):
if release_id in itoken.get(token,None).get("data",None):
return True
except Exception as e:
print(e)
return False
return False
def auto_clean_totken(self):
if self.flag.get("stat"):
t=Thread(target=self.clean_Token)
t.start()
self.flag["stat"]=0
def clean_Token(self):
while True:
ncount = len(self.TokenLists)
ntime = time.time()
for itokendic in self.TokenLists:
for token, data in itokendic.items():
if int(ntime) - int(data["atime"]) > 0:
self.TokenLists.remove(itokendic)
print("开始清理token,现在token:%s 清理token:%s 剩余token:%s"%(ncount,ncount-len(self.TokenLists),len(self.TokenLists)))
time.sleep(30)
def generateTokenForMedium(medium): def generateTokenForMedium(medium):
if medium == 'email': if medium == 'email':
return generateAlphanumericTokenOfLength(32) return generateAlphanumericTokenOfLength(32)

@ -133,9 +133,11 @@ def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
del_cache_response_by_short(appmobj.short,appmobj.app_id) del_cache_response_by_short(appmobj.short,appmobj.app_id)
appmobj.short = short appmobj.short = short
appmobj.name = appinfo["labelname"] appmobj.name = appinfo["labelname"]
appmobj.bundle_id = bundle_id
appmobj.save() appmobj.save()
except Exception as e: except Exception as e:
print(e) print(e)
appmobj.bundle_id = bundle_id
appmobj.name = appinfo["labelname"] appmobj.name = appinfo["labelname"]
appmobj.save() appmobj.save()

@ -0,0 +1,96 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 4月
# author: liuyu
# date: 2020/4/24
from api.utils.app.shellcmds import shell_command,use_user_pass
from fir_ser.settings import SUPER_SIGN_ROOT
import os
from api.utils.app.randomstrings import make_app_uuid
def exec_shell(cmd,remote=False):
if remote:
hostip="10.66.6.66"
port=65534
user="root"
passwd="root"
result = use_user_pass(hostip,port,user,passwd,cmd)
print(result)
return result
else:
result = shell_command(cmd)
print(result)
return result
class AppDeveloperApi(object):
def __init__(self,username,password,certid):
self.username = username
self.password = password
self.certid = certid
script_path=os.path.join(SUPER_SIGN_ROOT,'scripts','apple_api.rb')
self.cmd = "ruby %s '%s' '%s'" %(script_path,self.username,self.password)
def create_cert(self,user_obj):
cert_dir_name = make_app_uuid(user_obj,self.username)
cert_dir_path = os.path.join(SUPER_SIGN_ROOT,cert_dir_name)
if not os.path.isdir(cert_dir_path):
os.makedirs(cert_dir_path)
file_format_path_name = os.path.join(cert_dir_path,cert_dir_name)
self.cmd=self.cmd + " cert add '%s'" %(file_format_path_name)
result = exec_shell(self.cmd)
print(result)
def get_profile(self,bundleId,app_id,device_udid,device_name,provisionName):
self.cmd=self.cmd + " profile add %s %s %s %s %s '%s'" %(bundleId,app_id,device_udid,device_name,self.certid,provisionName)
result = exec_shell(self.cmd)
print(result)
def del_profile(self,bundleId,app_id):
self.cmd=self.cmd + " profile del %s %s" %(bundleId,app_id)
result = exec_shell(self.cmd)
print(result)
def set_device_status(self,status,device_udid):
if status == "enable":
self.cmd=self.cmd + " device enable %s" %(device_udid)
else:
self.cmd=self.cmd + " device disable %s" %(device_udid)
result = exec_shell(self.cmd)
print(result)
def add_device(self,device_udid,device_name):
self.cmd=self.cmd + " device add %s %s" %(device_udid,device_name)
result = exec_shell(self.cmd)
print(result)
def add_app(self,bundleId,app_id):
self.cmd=self.cmd + " app add %s %s" %(bundleId,app_id)
result = exec_shell(self.cmd)
print(result)
def del_app(self,bundleId):
self.cmd=self.cmd + " app del %s " %(bundleId)
result = exec_shell(self.cmd)
print(result)
# appdev = AppDeveloperApi("hehehuyu521@163.com","Hehehuyu123")
# appdev.create_cert()
# appdev.get_profile('com.hehegames.NenJiangMaJiang',"e6f0c1aeee6853af9c21651654a812e7","6dd86c12ab5506a1f7e45b0f58b6b1448f5cdf54","iPad5,1")
# # e6f0c1aeee6853af9c21651654a812e7_dis.mobileprovision
class ResignApp(object):
def __init__(self,my_local_key,app_dev_pem):
self.my_local_key=my_local_key
self.app_dev_pem = app_dev_pem
# script_path=os.path.join(SUPER_SIGN_ROOT,'scripts','apple_api.rb')
self.cmd = "isign -c %s -k %s " %(self.app_dev_pem,self.my_local_key)
def sign(self,new_profile,org_ipa,new_ipa):
self.cmd = self.cmd + " -p %s -o %s %s" %(new_profile,new_ipa,org_ipa)
result = exec_shell(self.cmd)
print(result)

@ -0,0 +1,127 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/6
import time,re,os
from subprocess import Popen, PIPE
import logging
import paramiko,json
import socket
def default_result():
return {'exit_code': '99', 'return_info': 'Failed to run, function_name is not existed'}
def portisonline(host,port):
result={}
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.settimeout(3)
result["host"] = host
result["port"]=port
result["functionname"]="CHECK_HOST_ONLINE:"
try:
sk.connect((host,port))
result["message"]="Connection success"
result["code"]=0
except Exception:
result["message"]="SSH Port Connection refused"
result["code"]=1
sk.close()
return result
class SSHConnection(object):
def __init__(self, host, port, username, password):
self._host = host
self._port = port
self._username = username
self._password = password
self._transport = None
self._sftp = None
self._client = None
self._connect()
def _connect(self):
transport = paramiko.Transport((self._host, self._port))
transport.connect(username=self._username, password=self._password)
self._transport = transport
#下载
def download(self, remotepath, localpath):
if self._sftp is None:
self._sftp = paramiko.SFTPClient.from_transport(self._transport)
self._sftp.get(remotepath, localpath)
self.close()
#上传
def put(self, localpath, remotepath):
if self._sftp is None:
self._sftp = paramiko.SFTPClient.from_transport(self._transport)
self.exec_command('mkdir -p %s'%(os.path.dirname(remotepath)))
self._sftp.put(localpath, remotepath)
self.close()
#执行命令
def exec_command(self, command):
if self._client is None:
self._client = paramiko.SSHClient()
self._client._transport = self._transport
stdin, stdout, stderr = self._client.exec_command(command)
data = stdout.read()
if len(data) > 0:
return {'status':0,'infos':data}
err = stderr.read()
if len(err) > 0:
return {'status':1,'infos':err}
return {'status': 0, 'infos': data}
def close(self):
if self._transport:
self._transport.close()
if self._client:
self._client.close()
def shell_command(cmdstrs):
cmdresult = re.split(r'\s+', cmdstrs)
result = default_result()
result['return_info'] = ''
shell_start_time = time.time()
child = Popen(cmdresult, shell=True, stdout=PIPE, stderr=PIPE)
out, err = child.communicate()
shell_end_time = time.time()
result['shell_run_time'] = shell_end_time - shell_start_time
out = out.strip(b'\n')
result['return_info'] = out
result['exit_code'] = child.returncode
logging.info(u'shell: %s - %s%s - %s%d' % (cmdstrs, 'return_info: ', out, 'exit_code: ', child.returncode))
return result
def use_user_pass(hostip,port,user,passwd,cmdstrs):
result = default_result()
checkinfo = portisonline(hostip, port)
if checkinfo["code"] != 0:
result['shell_run_time'] = 0
result['return_info'] = json.dumps(checkinfo)
result['exit_code'] = checkinfo["code"]
else:
conn = SSHConnection(hostip, port, user, passwd)
result['return_info'] = ''
shell_start_time = time.time()
out=conn.exec_command(cmdstrs)
shell_end_time = time.time()
result['shell_run_time'] = shell_end_time - shell_start_time
outs =str(out['infos'],'utf-8')
outs = outs.strip('\n')
result['return_info'] = outs
result['exit_code'] = out['status']
# logging.info(u'shell: %s - %s%s - %s%d' % (cmdstrs, 'return_info: ', out, 'exit_code: ', out['status']))
print('host: %s user:%s - shell: %s - %s%s - %s%d' % (hostip,user,cmdstrs, 'return_info: ', out, 'exit_code: ', out['status']))
conn.close()
return result

@ -0,0 +1,163 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/6
import uuid, xmltodict,os
from fir_ser.settings import SUPER_SIGN_ROOT,MEDIA_ROOT,SERVER_DOMAIN
from api.utils.app.iossignapi import AppDeveloperApi,ResignApp
from api.models import APPSuperSignUsedInfo,AppUDID,AppIOSDeveloperInfo,AppReleaseInfo
from api.utils.app.randomstrings import make_app_uuid,make_from_user_uuid
from django.db.models import F
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:
print(e)
return new_uuid_info
def make_udid_mobileconfig(udid_url, PayloadOrganization, PayloadUUID=uuid.uuid1(), PayloadDescription='本文件仅用来获取设备ID',
PayloadDisplayName='查询设备UDID'):
# <!--参考:https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/ConfigurationProfileExamples/ConfigurationProfileExamples.html-->
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, PayloadDisplayName, PayloadUUID, PayloadOrganization, PayloadDescription)
return mobileconfig
def get_post_udid_url(request, short):
server_domain = get_http_server_doamin(request)
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)
udid_url = "/".join(PATH_INFO_lists)
return udid_url
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
def get_redirect_server_domain(request):
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):
def __init__(self,udid_info,app_obj):
self.udid_info=udid_info
self.app_obj = app_obj
self.user_obj = app_obj.user_id
self.get_developer_auth()
def get_developer_auth(self):
developer_obj = AppIOSDeveloperInfo.objects.filter(user_id=self.user_obj,is_actived=True,use_number__lte=F("usable_number")).first()
auth = {
"username":developer_obj.email,
"password": developer_obj.password,
"certid":developer_obj.certid
}
self.auth = auth
def download_profile(self):
app_api_obj = AppDeveloperApi(** self.auth)
bundleId = self.app_obj.bundle_id
app_id=self.app_obj.app_id
device_udid= self.udid_info.get('udid')
device_name=self.udid_info.get('product')
app_api_obj.get_profile(bundleId,app_id,device_udid,device_name,self.get_profile_full_path())
def get_profile_full_path(self):
cert_dir_name = make_app_uuid(self.user_obj,self.auth.get("username"))
cert_dir_path = os.path.join(SUPER_SIGN_ROOT,cert_dir_name)
file_format_path_name = os.path.join(cert_dir_path,cert_dir_name,"profile")
if not os.path.isdir(file_format_path_name):
os.makedirs(file_format_path_name)
provisionName = os.path.join(file_format_path_name,self.app_obj.app_id)
return provisionName+ '.mobileprovision'
def resign(self):
self.download_profile()
cert_dir_name = make_app_uuid(self.app_obj.user_id,self.auth.get("username"))
cert_dir_path = os.path.join(SUPER_SIGN_ROOT,cert_dir_name)
if not os.path.isdir(cert_dir_path):
os.makedirs(cert_dir_path)
file_format_path_name = os.path.join(cert_dir_path,cert_dir_name)
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)
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")
ResignAppObj.sign(self.get_profile_full_path(),org_file,new_file)
newdata={
"is_signed":True,
"binary_file":random_file_name + ".ipa"
}
AppUDID.objects.filter(app_id=self.app_obj,udid=self.udid_info.get('udid')).update(**newdata)

@ -82,7 +82,7 @@ class AppsSerializer(serializers.ModelSerializer):
class AppsShortSerializer(serializers.ModelSerializer): class AppsShortSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.Apps model = models.Apps
fields = ["app_id","name","short","has_combo","isshow","description","need_password",'master_release','type'] fields = ["app_id","name","short","has_combo","isshow","description","need_password",'master_release','type','issupersign']
need_password=serializers.SerializerMethodField() need_password=serializers.SerializerMethodField()

@ -13,11 +13,15 @@ from api.utils.storage.storage import Storage,LocalStorage
from api.utils.crontab.sync_cache import sync_download_times_by_app_id from api.utils.crontab.sync_cache import sync_download_times_by_app_id
from api.utils.crontab import run from api.utils.crontab import run
def get_download_url_by_cache(app_obj, filename, limit, isdownload=True,key=''): def get_download_url_by_cache(app_obj, filename, limit, isdownload=True,key='',udid=None):
now = time.time() now = time.time()
if isdownload is None: if isdownload is None:
local_storage = LocalStorage('localhost', False) local_storage = LocalStorage('localhost', False)
return local_storage.get_download_url(filename, limit, 'plist') download_url_type = 'plist'
if not udid:
if app_obj.get('issupersign',None):
download_url_type = 'mobileconifg'
return local_storage.get_download_url(filename, limit, download_url_type)
down_key = "_".join([key.lower(), CACHE_KEY_TEMPLATE.get('download_url_key'), filename]) down_key = "_".join([key.lower(), CACHE_KEY_TEMPLATE.get('download_url_key'), filename])
download_val = cache.get(down_key) download_val = cache.get(down_key)
if download_val: if download_val:
@ -33,7 +37,7 @@ def get_app_instance_by_cache(app_id,password, limit):
app_key="_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"),app_id]) app_key="_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"),app_id])
app_obj_cache = cache.get(app_key) app_obj_cache = cache.get(app_key)
if not app_obj_cache: if not app_obj_cache:
app_obj_cache = Apps.objects.filter(app_id=app_id).values("pk", 'user_id', 'type','password').first() app_obj_cache = Apps.objects.filter(app_id=app_id).values("pk", 'user_id', 'type','password','issupersign').first()
cache.set(app_key, app_obj_cache, limit) cache.set(app_key, app_obj_cache, limit)
app_password=app_obj_cache.get("password") app_password=app_obj_cache.get("password")
@ -60,7 +64,7 @@ def set_app_download_by_cache(app_id, limit=900):
set_app_today_download_times(app_id) set_app_today_download_times(app_id)
return download_times + 1 return download_times + 1
def del_cache_response_by_short(short,app_id): def del_cache_response_by_short(short,app_id,udid=''):
cache.delete("_".join([CACHE_KEY_TEMPLATE.get("download_short_key"),short])) cache.delete("_".join([CACHE_KEY_TEMPLATE.get("download_short_key"),short]))
key = "_".join([CACHE_KEY_TEMPLATE.get("download_short_key"),short,'*']) key = "_".join([CACHE_KEY_TEMPLATE.get("download_short_key"),short,'*'])
for app_download_key in cache.iter_keys(key): for app_download_key in cache.iter_keys(key):
@ -71,9 +75,9 @@ def del_cache_response_by_short(short,app_id):
key='ShortDownloadView'.lower() key='ShortDownloadView'.lower()
master_release_dict = AppReleaseInfo.objects.filter(app_id__app_id=app_id,is_master=True).values('icon_url','release_id').first() master_release_dict = AppReleaseInfo.objects.filter(app_id__app_id=app_id,is_master=True).values('icon_url','release_id').first()
download_val = CACHE_KEY_TEMPLATE.get('download_url_key') download_val = CACHE_KEY_TEMPLATE.get('download_url_key')
cache.delete("_".join([key, download_val, os.path.basename(master_release_dict.get("icon_url"))])) cache.delete("_".join([key, download_val, os.path.basename(master_release_dict.get("icon_url")),udid]))
cache.delete("_".join([key,download_val, master_release_dict.get('release_id')])) cache.delete("_".join([key,download_val, master_release_dict.get('release_id'),udid]))
cache.delete("_".join([key, CACHE_KEY_TEMPLATE.get("make_token_key"), master_release_dict.get('release_id')])) cache.delete("_".join([key, CACHE_KEY_TEMPLATE.get("make_token_key"), master_release_dict.get('release_id'),udid]))
def del_cache_by_app_id(app_id,user_obj): def del_cache_by_app_id(app_id,user_obj):

@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.

@ -169,8 +169,11 @@ 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)
if apps_obj.type == 1:
apps_obj.issupersign = data.get("issupersign", apps_obj.issupersign)
apps_obj.save() apps_obj.save()
except Exception as e: except Exception as e:
print(e)
res.code = 1005 res.code = 1005
res.msg = "短连接已经存在" res.msg = "短连接已经存在"

@ -10,13 +10,13 @@ from fir_ser import settings
from api.utils.TokenManager import DownloadToken from api.utils.TokenManager import DownloadToken
from api.utils.app.randomstrings import make_random_uuid from api.utils.app.randomstrings import make_random_uuid
from api.utils.app.apputils import make_resigned from api.utils.app.apputils import make_resigned
from api.utils.app.supersignutils import make_udid_mobileconfig,udid_bytes_to_dict,get_post_udid_url,get_redirect_server_domain,IosUtils
from api.utils.storage.storage import Storage from api.utils.storage.storage import Storage
from api.utils.storage.caches import get_app_instance_by_cache,get_download_url_by_cache,set_app_download_by_cache from api.utils.storage.caches import get_app_instance_by_cache,get_download_url_by_cache,set_app_download_by_cache,del_cache_response_by_short
import os import os
from rest_framework_extensions.cache.decorators import cache_response from rest_framework_extensions.cache.decorators import cache_response
from api.utils.serializer import AppsShortSerializer from api.utils.serializer import AppsShortSerializer
from api.models import Apps,AppReleaseInfo from api.models import Apps,AppReleaseInfo,AppUDID
from django.http import FileResponse from django.http import FileResponse
class DownloadView(APIView): class DownloadView(APIView):
''' '''
@ -56,16 +56,24 @@ class DownloadView(APIView):
name = release_obj.app_id.name name = release_obj.app_id.name
ios_plist_bytes = make_resigned(storage.get_download_url(filename),storage.get_download_url(release_obj.icon_url),bundle_id,app_version,name) ios_plist_bytes = make_resigned(storage.get_download_url(filename),storage.get_download_url(release_obj.icon_url),bundle_id,app_version,name)
response = FileResponse(ios_plist_bytes) response = FileResponse(ios_plist_bytes)
response['content_type'] = "application/x-plist" response['Content-Type'] = "application/x-plist"
response['Content-Disposition'] = 'attachment; filename=' + make_random_uuid() response['Content-Disposition'] = 'attachment; filename=' + make_random_uuid()
return response return response
elif ftype == 'mobileconifg':
release_obj = AppReleaseInfo.objects.filter(release_id=filename.split('.')[0]).first()
if release_obj:
bundle_id = release_obj.app_id.bundle_id
udid_url = get_post_udid_url(request,release_obj.app_id.short)
ios_udid_mobileconfig = make_udid_mobileconfig(udid_url, bundle_id)
response = FileResponse(ios_udid_mobileconfig)
response['Content-Type'] = "application/x-apple-aspen-config"
response['Content-Disposition'] = 'attachment; filename=' + make_random_uuid() +'.mobileconfig'
return response
res.code=1004 res.code=1004
res.msg="token校验失败" res.msg="token校验失败"
return Response(res.dict) return Response(res.dict)
class ShortDownloadView(APIView): class ShortDownloadView(APIView):
''' '''
根据下载短链接获取应用信息 根据下载短链接获取应用信息
@ -75,7 +83,10 @@ class ShortDownloadView(APIView):
def get(self,request,short): def get(self,request,short):
res = BaseResponse() res = BaseResponse()
release_id = request.query_params.get("release_id", None) release_id = request.query_params.get("release_id", None)
udid = request.query_params.get("udid", None)
app_obj = Apps.objects.filter(short=short).first() app_obj = Apps.objects.filter(short=short).first()
if udid:
del_cache_response_by_short(short,app_obj.app_id,udid=udid)
if not app_obj: if not app_obj:
res.code=1003 res.code=1003
res.msg="该应用不存在" res.msg="该应用不存在"
@ -88,13 +99,15 @@ class ShortDownloadView(APIView):
app_serializer = AppsShortSerializer(app_obj,context={"key":"ShortDownloadView","release_id":release_id,"storage":Storage(app_obj.user_id)}) app_serializer = AppsShortSerializer(app_obj,context={"key":"ShortDownloadView","release_id":release_id,"storage":Storage(app_obj.user_id)})
res.data = app_serializer.data res.data = app_serializer.data
res.udid = udid
return Response(res.dict) return Response(res.dict)
#key的设置 #key的设置
def calculate_cache_key(self, view_instance, view_method, def calculate_cache_key(self, view_instance, view_method,
request, args, kwargs): request, args, kwargs):
release_id = request.query_params.get("release_id", '') release_id = request.query_params.get("release_id", '')
return "_".join([settings.CACHE_KEY_TEMPLATE.get("download_short_key"), kwargs.get("short", ''),release_id]) udid = request.query_params.get("udid", '')
return "_".join([settings.CACHE_KEY_TEMPLATE.get("download_short_key"), kwargs.get("short", ''),release_id,udid])
class InstallView(APIView): class InstallView(APIView):
''' '''
@ -108,6 +121,7 @@ class InstallView(APIView):
release_id = request.query_params.get("release_id",None) release_id = request.query_params.get("release_id",None)
isdownload = request.query_params.get("isdownload",None) isdownload = request.query_params.get("isdownload",None)
password = request.query_params.get("password",None) password = request.query_params.get("password",None)
udid = request.query_params.get("udid",None)
if not downtoken or not short or not release_id: if not downtoken or not short or not release_id:
res.code=1004 res.code=1004
@ -125,9 +139,9 @@ class InstallView(APIView):
else: else:
apptype = '.ipa' apptype = '.ipa'
if isdownload : if isdownload :
download_url = get_download_url_by_cache(app_obj,release_id + apptype, 600) download_url = get_download_url_by_cache(app_obj,release_id + apptype, 600,udid=udid)
else: else:
download_url = get_download_url_by_cache(app_obj,release_id + apptype,600,isdownload) download_url = get_download_url_by_cache(app_obj,release_id + apptype,600,isdownload,udid=udid)
res.data={"download_url":download_url} res.data={"download_url":download_url}
return Response(res.dict) return Response(res.dict)
@ -140,3 +154,35 @@ class InstallView(APIView):
res.msg = "该应用不存在" res.msg = "该应用不存在"
return Response(res.dict) return Response(res.dict)
from django.views import View
from django.http import HttpResponsePermanentRedirect,Http404
class IosUDIDView(View):
def post(self,request,short):
stream_f = str(request.body)
format_udid_info = udid_bytes_to_dict(stream_f)
print(format_udid_info,short)
try:
app_info=Apps.objects.filter(short=short).values("pk",'issupersign').first()
if app_info :
if app_info.get('issupersign'):
AppUDID.objects.update_or_create(app_id_id= app_info.get('pk'),**format_udid_info,defaults={'udid':format_udid_info.get('udid')})
ios_obj = IosUtils(format_udid_info,app_info)
ios_obj.resign()
else:
return Http404
else:
return Http404
except Exception as e:
print(e)
server_domain = get_redirect_server_domain(request)
return HttpResponsePermanentRedirect("%s/%s?udid=%s"%(server_domain,short,format_udid_info.get("udid")))

@ -145,6 +145,8 @@ STATICFILES_DIRS=[
# Media配置 # Media配置
MEDIA_URL = "files/" MEDIA_URL = "files/"
MEDIA_ROOT = os.path.join(BASE_DIR, "files") MEDIA_ROOT = os.path.join(BASE_DIR, "files")
#supersign配置
SUPER_SIGN_ROOT = os.path.join(BASE_DIR, "supersign")
AUTH_USER_MODEL="api.UserInfo" AUTH_USER_MODEL="api.UserInfo"
@ -247,5 +249,10 @@ SYNC_CACHE_TO_DATABASE={
'download_times':10 #下载次数同步时间 'download_times':10 #下载次数同步时间
} }
SERVER_DOMAIN={
'POST_UDID_DOMAIN':'http://192.168.8.236:8000', #超级签名调试
'REDIRECT_UDID_DOMAIN': 'http://192.168.8.236:8080', # 超级签名调试
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default" SESSION_CACHE_ALIAS = "default"

@ -0,0 +1,157 @@
#!/usr/bin/ruby
# -*- coding: UTF-8 -*-
require "spaceship"
class DevelopPortalHandle
def login(username,password)
Spaceship::Portal.login(username,password)
end
def createCert(file_format_path_name)
csr, pkey = Spaceship::Portal.certificate.create_certificate_signing_request
cert=Spaceship::Portal.certificate.production.create!(csr: csr)
File.write(file_format_path_name+".key",pkey)
File.write(file_format_path_name+".cer",cert.download_raw)
File.write(file_format_path_name+".pem",cert.download)
File.write(file_format_path_name+".info",cert)
end
def createApp(appid,appname)
app = Spaceship::Portal.app.find(appid)
if !app then
app = Spaceship::Portal.app.create!(bundle_id: appid, name: appname)
app.update_service(Spaceship::Portal.app_service.push_notification.on)
app.update_service(Spaceship::Portal.app_service.vpn_configuration.on)
end
end
def deleteApp(appid)
app = Spaceship::Portal.app.find(appid)
if app then
app.delete!
end
end
#appstore or inHouse
def createDistributionProvision(provisioningClass,appid,provisionName,certid)
cert = Spaceship::Portal.certificate.Production.find(id=certid)
if !cert then
cert = Spaceship::Portal.certificate.production.all.last
end
profile = provisioningClass.create!(bundle_id: appid,certificate:cert,name:provisionName + '_dis')
return profile
end
#appstore or inHouse
def downloadDistributionProvision(provisioningClass,appid,provisionName,certid)
#查找有没有provision文件
filtered_profiles = provisioningClass.find_by_bundle_id(bundle_id: appid)
profile = nil
if 0 < filtered_profiles.length then
profile = filtered_profiles[0]
elsif 0 == filtered_profiles.length then
profile = createDistributionProvision(provisioningClass,appid,provisionName,certid)
end
#没有找到就创建,找到就下载
File.write(provisionName, profile.download)
return provisionFileName
end
def delete_profile(provisioningClass,appid)
filtered_profiles = provisioningClass.find_by_bundle_id(bundle_id: appid)
profile = nil
if 0 < filtered_profiles.length then
profile = filtered_profiles[0]
profile.delete!
end
end
def addDevice(device_name,device_udid)
device = Spaceship::Portal.device.find_by_udid(device_udid, include_disabled: false)
if !device then
Spaceship::Portal.device.create!(name: device_name, udid: device_udid)
end
end
def enableDevice(device_udid)
device=Spaceship::Portal.device.find_by_udid(device_udid, include_disabled: true)
if device then
device.enable!
end
end
def disableDevice(device_udid)
device = Spaceship::Portal.device.find_by_udid(device_udid)
if device then
device.disable!
end
end
end
handle = DevelopPortalHandle.new()
handle.login(ARGV[0],ARGV[1])
function = ARGV[2]
case function
when "device"
action = ARGV[3]
device_udid = ARGV[4]
device_name = ARGV[5]
case action
when "add"
device_name = device_udid+','+device_udid
handle.addDevice(device_name,device_udid)
when "enable"
handle.enableDevice(device_udid)
when "disable"
handle.disableDevice(device_udid)
end
when "app"
action = ARGV[3]
app_id = ARGV[4]
app_name = ARGV[5]
appid = app_id+app_name
case action
when "add"
handle.createApp(appid,app_name)
when "del"
handle.deleteApp(appid)
handle.delete_profile(Spaceship::Portal.provisioning_profile.ad_hoc,appid)
end
when "profile"
action = ARGV[3]
app_id = ARGV[4]
app_name = ARGV[5]
appid = app_id+app_name
case action
when "add"
device_udid = ARGV[6]
device_name = ARGV[7]
certid = ARGV[8]
provisionName = ARGV[9]
handle.createApp(appid,app_name)
handle.addDevice(device_name,device_udid)
provisionPath = handle.downloadDistributionProvision(Spaceship::Portal.provisioning_profile.ad_hoc,appid,provisionName,certid)
when "del"
handle.delete_profile(Spaceship::Portal.provisioning_profile.ad_hoc,appid)
end
when "cert"
action = ARGV[3]
file_format_path_name = ARGV[4]
case action
when "add"
handle.createCert(file_format_path_name)
end
else
puts "error"
end
Loading…
Cancel
Save