Exemple #1
0
def __add_task(_user: UserInfo):
    _account = IosAccountInfo.objects.get(account=_user.account)
    _project = IosProjectInfo.objects.get(project=_user.project)
    _profile = IosProfileInfo.objects.get(sid="%s:%s" %
                                          (_user.account, _user.project))
    _cert = IosCertInfo.objects.get(
        sid="%s:%s" % (_user.account, str_json_a(_profile.certs)[0]))
    Assert(_profile, "[%s][%s]证书无效" % (_project.project, _account.account))
    Assert(_project.md5sum, "项目[%s]原始ipa还没上传" % _project.project)
    Assert(_cert.cert_p12, "项目[%s]p12还没上传" % _project.project)
    Log("[%s]发布任务[%s]" % (_user.project, _user.account))
    _task, _ = TaskInfo.objects.get_or_create(uuid=_user.uuid)
    _task.state = "none"
    _task.worker = ""
    _task.save()
    resign_ipa.delay(
        **{
            "uuid": _user.uuid,
            "cert": "iPhone Developer: %s" % _cert.name,
            "cert_url": entry("/apple/download_cert?uuid=%s" % _user.uuid),
            "cert_md5": md5bytes(base64decode(_cert.cert_p12)),
            "mp_url": entry("/apple/download_mp?uuid=%s" % _user.uuid),
            "mp_md5": md5bytes(base64decode(_profile.profile)),
            "project": _project.project,
            "ipa_url": entry("/projects/%s/orig.ipa" % _user.project),
            "ipa_md5": _project.md5sum,
            "ipa_new": "%s_%s.ipa" % (_account.team_id, _account.devices_num),
            "upload_url": entry("/apple/upload_ipa?uuid=%s" % _user.uuid),
            "process_url": entry("/apple/task_state?uuid=%s" % _user.uuid),
        })
Exemple #2
0
def mongo_pack_set(key: str, value: dict, model: str, size=1 * 1000 * 1000):
    """
    大于16M的插入
    以多个对象存在
    """
    db = mongo(model)
    v = json_str(value)
    if len(v) < size:
        return mongo_set(key, value, model)
    # 需要切一下
    Assert("__pack__" not in value, "数据内不能有 __pack__ 字段")
    Assert("__no__" not in value, "数据内不能有 __no__ 字段")
    Assert("__value__" not in value, "数据内不能有 __value__ 字段")
    Assert("__length__" not in value, "数据内不能有 __length__ 字段")

    pack_length = int(ceil(len(v) / size))
    new_value = list(
        map(
            lambda x: {
                "__pack__": True,
                "__no__": v,
                "__length__": pack_length,
                "__value__": v[x * size:(x + 1) * size],
            }, range(0, pack_length)))

    if mongo(model).find_one({
            "_id": "%s_%s" % (model, key)
    }, ["__pack__", "__length__"]).get("__length__", 0) > pack_length:
        # 删除旧的部分
        mongo_pack_pop(key, model)

    mongo_set(key, new_value[0], model)
    for each in new_value[1:]:
        mongo_set(key, each, model)
    return True
Exemple #3
0
def security_code_sms(phone: str, sms: str):
    Assert("apple" in sms.lower(), "短信非验证码短信[%s]" % sms)
    code = re.compile(r"\d{4,6}").findall(sms)
    Assert(len(code), "短信非验证码短信[%s]" % sms)
    code = code[0]
    _account = IosAccountInfo.objects.filter(
        phone=phone).first()  # type: IosAccountInfo
    publish_security_code(_account.account if _account else "*", code, now())
    return {
        "succ": True,
    }
Exemple #4
0
def __add_task(_user: UserInfo):
    _account = IosAccountInfo.objects.filter(
        account=_user.account).first()  # type:IosAccountInfo
    _project = IosProjectInfo.objects.filter(
        project=_user.project).first()  # type:IosProjectInfo
    _profile = IosProfileInfo.objects.filter(
        sid="%s:%s" %
        (_user.account, _user.project)).first()  # type:IosProfileInfo
    Assert(_profile, "[%s][%s]证书无效" % (_project.project, _account.account))
    Log("[%s]发布任务[%s]" % (_user.project, _user.account))
    db_session.publish(
        "task:package",
        json_str({
            "cert":
            "iPhone Developer: zhangming luo",
            "cert_p12":
            "",
            "mp_url":
            entry("/apple/download_mp?uuid=%s" % _user.uuid),
            "mp_md5":
            md5bytes(base64decode(_profile.profile)),
            "project":
            _project.project,
            "ipa_url":
            _asset_url("%s/orig.ipa" % _user.project),
            "ipa_md5":
            _project.md5sum,
            "ipa_new":
            "%s_%s.ipa" % (_account.team_id, _account.devices_num),
            "upload_url":
            entry("/apple/upload_ipa?project=%s&account=%s" %
                  (_user.project, _user.account)),
            "ts":
            now(),
        }))
Exemple #5
0
def task_state(uuid: str, worker: str = "", state: str = "", auto_start=True):
    _task, _ = TaskInfo.objects.get_or_create(uuid=uuid)
    if state:
        if _task.worker:
            Assert(_task.worker == worker,
                   "越权更改任务[%s]状态[%s]=>[%s]" % (uuid, _task.worker, worker))
        else:
            _task.worker = worker
            _task.save()
        if _task.state != state:
            Log("任务[%s]状态变更[%s]=>[%s]" % (uuid, _task.state, state))
            _task.state = state
            _task.save()
        return {
            "succ": True,
        }
    else:
        if auto_start and _task.state in {"fail", "expire", "none", ""}:
            # noinspection PyTypeChecker
            rebuild({
                "uuid": uuid,
            })
        # 获取当前的状态
        return {
            "code":
            1 if _task.state in {"fail", "expire"} else 0,
            "finished":
            _task.state == "succ",
            "progress":
            "%d%%" % ((_states.index(_task.state) + 1) * 100 / len(_states))
            if _task.state in _states else "0%",
        }
Exemple #6
0
def newbee(project: str):
    """
    根据项目生成具体的一个可以注册新设备的uuid
    """
    _info = IosProjectInfo.objects.filter(
        project=project).first()  # type: IosProjectInfo
    Assert(_info is not None, "找不到对应的项目[%s]" % project)
    return {
        "uuid": _newbee(_info),
    }
Exemple #7
0
def _get_cert(info: IosAccountInfo) -> IosCertInfo:
    cert = IosCertInfo.objects.filter(
        app=info.account,
        expire__gt=datetime.datetime.utcfromtimestamp(now() // 1000),
        type_str="development",
    ).first()  # type: IosCertInfo
    if not cert:
        # todo: 生成证书
        pass
    return Assert(cert, "缺少现成的开发[iOS App Development]证书[%s]" % info.account)
Exemple #8
0
def upload_cert_p12(account: str, file: bytes, password: str = "q1w2e3r4"):
    p12 = crypto.load_pkcs12(file, password)
    name = re.match(r"iPhone Developer: (.+) \([A-Z0-9]+\)",
                    p12.get_friendlyname().decode("utf8")).groups()
    Assert(len(name), "非法的p12文件")
    name = name[0]  # type: str
    _cert = IosCertInfo.objects.get(sid="%s:%s" % (account, name))
    _cert.cert_p12 = base64(file)
    _cert.save()
    return {
        "succ": True,
    }
Exemple #9
0
def _get_app(_config: IosAccountHelper, project: str) -> IosAppInfo:
    app = IosAppInfo.objects.filter(
        sid="%s:%s" % (_config.account, project), ).first()  # type: IosAppInfo
    if not app:
        _project = IosProjectInfo.objects.filter(
            project=project).first()  # type: IosProjectInfo
        _identifier = "%s.%s" % (_project.bundle_prefix, GetRandomName())
        _config.post(
            "验证一个app",
            "https://developer.apple.com/services-account/QH65B2/account/ios/identifiers/validateAppId.action?teamId=",
            data={
                "teamId": _config.team_id,
                "appIdName": "ci%s" % project,
                "appIdentifierString": _identifier,
                "type": "explicit",
                "explicitIdentifier": _identifier,
            },
        )
        _config.post(
            "注册一个app",
            "https://developer.apple.com/services-account/v1/bundleIds",
            data=json_str({
                "data": {
                    "type": "bundleIds",
                    "attributes": {
                        "name": "ci%s" % project,
                        "identifier": _identifier,
                        "platform": "IOS",
                        "seedId": _config.team_id,
                        "teamId": _config.team_id,
                    },
                    "relationships": {
                        "bundleIdCapabilities": {
                            "data":
                            tran(get_capability,
                                 str_json(_project.capability)),
                        },
                    },
                },
            }),
            ex_headers={
                "content-type": "application/vnd.api+json",
            },
            csrf=True,
            json_api=False,
            status=201,
        )
        __list_all_app(_config, project)
        app = IosAppInfo.objects.filter(
            sid="%s:%s" %
            (_config.account, project), ).first()  # type: IosAppInfo
    return Assert(app, "账号[%s]缺少app[%s]" % (_config.account, project))
Exemple #10
0
def upload_ipa(project: str, account: str, file: bytes):
    base = os.path.join("static/income", project)
    os.makedirs(base, exist_ok=True)
    _info = IosAccountInfo.objects.filter(
        account=account).first()  # type:IosAccountInfo
    Assert(_info, "账号[%s]不存在" % account)
    filename = "%s_%s.ipa" % (_info.team_id, _info.devices_num)
    with open(os.path.join(base, filename), mode="wb") as fout:
        fout.write(file)
    Log("[%s]收到新打包的ipa[%s]" % (account, filename))
    return {
        "succ": True,
    }
Exemple #11
0
def download_profile(uuid: str):
    """
    基于用户id下载
    """
    _user = UserInfo.objects.filter(uuid=uuid).first()  # type: UserInfo
    Assert(_user is not None, "没有找到uuid[%s]" % uuid)
    _config = IosAccountHelper(
        IosAccountInfo.objects.filter(account=_user.account).first())
    _info = IosProfileInfo.objects.filter(
        sid="%s" % _config.account).first()  # type: IosProfileInfo
    return {
        "encodedProfile": _info.profile,
    }
Exemple #12
0
def login_by_fastlane(_req: HttpRequest, cmd: str = "", account: str = ""):
    """
    通过命令获取会话
    fastlane spaceauth -u [email protected]
    ex.
    export FASTLANE_SESSION='---\n- !ruby/object:HTTP::Cookie\n  name: myacinfo\n  value: DAWTKNV2e32a0823580640561dc9dfd382265048c32c2aa5b04485930b2ada1d1c7eba28dee6c6065c749f708f2a429f8f9f2d0f2f7d2ad629652ca5bc3383c0772d51c6ca34a2f7b09141f7b19c358c2b25d0738079ab1e48a06610228a574342c84ef13349ede1a012c25155e265a17989a3b09631dd953954505153fb7ef71aecfe303530cb684c89e8402cb82d8d4d93c3fc3c1209e334f65f71c7ae0cfdf0349ec757abcb104a591f5bea25ac0f1207004c19645b80ed82fb5cd0d3a740224b2f3aef9e91b049bb63a94ae3c76d027411f077069865209d733617a7a84f54cf7e9488e9b4f0a918d29f184f5ec76d95b5f55def61682f70b7f10fc12dc43d6e380213dd1f702a4f3ccab3ad438cd0f6a87c295e028a12ec410aa3fa689210d040377995914d4d3718b90f85ad5452d5db47ef9ae11c6b3216cf8ab61025adc203b0bf072ce832240c384d83f0f4aaf477a3c7313de4c20c5e32c530ff1ad76aebcd8538ac485a9a46941dfa94ee2f3fb40e38666533326562333665333834343461323366383636666563303166613533303330656361323836MVRYV2\n  domain: apple.com\n  for_domain: true\n  path: "/"\n  secure: true\n  httponly: true\n  expires: \n  max_age: \n  created_at: 2019-03-15 11:55:51.031441000 +08:00\n  accessed_at: 2019-03-15 11:55:51.041509000 +08:00\n- !ruby/object:HTTP::Cookie\n  name: dqsid\n  value: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJzZnp2ZGFldGJPeWpXaTc1LVpTRmNBIn0.IEYqXF-pxIYdIwP2rdNRNhoxdCzJgGxt4olTZa2fXo8\n  domain: olympus.itunes.apple.com\n  for_domain: false\n  path: "/"\n  secure: true\n  httponly: true\n  expires: \n  max_age: 1800\n  created_at: &1 2019-03-15 11:55:52.798977000 +08:00\n  accessed_at: *1\n
    curl localhost:8000/apple/login_by_fastlane --data $FASTLANE_SESSION
    """
    if not cmd:
        cmd = _req.body.decode("utf-8")
    Assert(len(cmd), "命令行不对")
    cmd = cmd.replace("\\n", "")
    cookie = dict(re.compile(r"name:\s+(\S+)\s+value:\s+(\S+)").findall(cmd))
    if not account:
        rsp = requests.post(
            "https://developer.apple.com/services-account/QH65B2/account/getUserProfile",
            headers={"cookie": to_form_url(cookie, split=';')})
        if rsp.status_code != 200:
            return {
                "succ": False,
                "reason": "无效的fastlane export",
            }
        data = rsp.json()
        account = data["userProfile"]["email"]

    if account:
        _info = IosAccountInfo.objects.filter(
            account=account).first()  # type:IosAccountInfo
        if not _info:
            _info = IosAccountInfo()
            _info.account = account
            _info.save()
        _info.cookie = json_str(cookie)
        _info.headers = json_str({})
        _info.save()
        Log("通过fastlane登录[%s]" % account)
        return {
            "succ": True,
            "msg": "登录[%s]成功" % account,
        }
    else:
        return {
            "succ": False,
            "msg": "请求不具备提取登录信息",
        }
Exemple #13
0
def _package(orig_file, provisioning_profile, certificate, output_path):
    base = tempfile.mkdtemp()
    ipa_file = os.path.join(base, "orig.ipa")
    mp_file = os.path.join(base, "mp.ipa")
    os.symlink(os.path.abspath(orig_file), ipa_file)
    os.symlink(os.path.abspath(provisioning_profile), mp_file)
    Log("准备ipa[%s]" % ipa_file)
    # with Block("打包", fail=False):
    #     app = app_argument(ipa_file).unpack_to_dir(base)
    #     Log("准备provision")
    #     app.provision(mp_file)
    #     Log("构造entitlements")
    #     app.create_entitlements()
    #     Log("签名证书")
    #     app.sign(certificate)
    #     Log("重新封包")
    #     app.package(output_path)
    #     Log("ipa[%s]" % output_path)
    try:
        if not os.path.isfile(output_path) or True:
            # 方案二打包
            sh_file = os.path.join(base, "provisions.sh")
            if os.path.exists("provisions.sh"):
                os.symlink(os.path.abspath("provisions.sh"), sh_file)
            else:
                os.symlink(os.path.abspath("tools/provisions.sh"), sh_file)
            _shell_run("%s -p %s -c '%s' %s " %
                       (sh_file, mp_file, certificate, ipa_file),
                       succ_only=True,
                       verbose=True,
                       debug=True,
                       pwd=base)
            Assert(os.path.isfile(os.path.join(base, "stage", "out.ipa")),
                   "打包脚本失败了")
            os.rename(os.path.join(base, "stage", "out.ipa"),
                      os.path.abspath(output_path))

        if not os.path.isfile(output_path):
            raise Fail("打包失败")
    finally:
        shutil.rmtree(base, ignore_errors=True)
Exemple #14
0
def upload_ipa(worker: str, uuid: str, file: bytes):
    _user = UserInfo.objects.get(uuid=uuid)
    project = _user.project
    account = _user.account
    base = os.path.join("static/income", project)
    os.makedirs(base, exist_ok=True)
    _info = IosAccountInfo.objects.filter(
        account=account).first()  # type:IosAccountInfo
    Assert(_info, "账号[%s]不存在" % account)
    filename = "%s_%s.ipa" % (_info.team_id, _info.devices_num)
    with open(os.path.join(base, filename), mode="wb") as fout:
        fout.write(file)
    Log("[%s]收到新打包的ipa[%s]" % (account, filename))
    # todo: 遍历所有的设备关联的包?
    _task, _ = TaskInfo.objects.get_or_create(uuid=uuid)
    if _task.worker in {worker, "none"}:
        _task.state = "succ"
        _task.worker = worker
        _task.size = len(file)
        _task.save()
    return {
        "succ": True,
    }
Exemple #15
0
def login_by_curl(_req: HttpRequest, cmd: str = "", account: str = ""):
    """
    通过拦截网页的curl 请求
    https://developer.apple.com/account/getUserProfile
    ex.
    curl 'https://developer.apple.com/services-account/QH65B2/account/getUserProfile' -H 'origin: https://developer.apple.com' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: zh-CN,zh;q=0.9' -H 'csrf: cf0796aee015fe0f03e7ccc656ba4b898b696cc1072027988d89b1f6e607fd67' -H 'cookie: geo=SG; ccl=SR+vWVA/vYTrzR1LkZE2tw==; s_fid=56EE3E795513A2B4-16F81B5466B36881; s_cc=true; s_vi=[CS]v1|2E425B0B852E2C90-40002C5160000006[CE]; dslang=CN-ZH; site=CHN; s_pathLength=developer%3D2%2C; acn01=v+zxzKnMyleYWzjWuNuW1Y9+kAJBxfozY2UAH0paNQB+FA==; myacinfo=DAWTKNV2a5c238e8d27e8ed221c8978cfb02ea94b22777f25ffec5abb1a855da8debe4f59d60b506eae457dec4670d5ca9663ed72c3d1976a9f87c53653fae7c63699abe64991180d7c107c50ce88be233047fc96de200c3f23947bfbf2e064c7b9a7652002d285127345fe15adf53bab3d347704cbc0a8b856338680722e5d0387a5eb763d258cf19b79318be28c4abd01e27029d2ef26a1bd0dff61d141380a1b496b92825575735d0be3dd02a934db2d788c9d6532b6a36bc69d244cc9b4873cef8f4a3a90695f172f6f521330f67f20791fd7d62dfc9d6de43899ec26a8485191d62e2c5139f81fca2388d57374ff31f9f689ad373508bcd74975ddd3d3b7875fe3235323962636433633833653433363562313034383164333833643736393763303538353038396439MVRYV2; DSESSIONID=1c3smahkpfbkp7k3fju30279uoba8p8480gs5ajjgsbbvn8lubqt; s_sq=%5B%5BB%5D%5D' -H 'user-locale: en_US' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36 QQBrowser/4.5.122.400' -H 'content-type: application/json' -H 'accept: application/json' -H 'cache-control: max-age=0, no-cache, no-store, must-revalidate, proxy-revalidate' -H 'authority: developer.apple.com' -H 'referer: https://developer.apple.com/account/' -H 'csrf_ts: 1552204197631' --data-binary '{}' --compressed
    """

    if not cmd:
        cmd = _req.body.decode("utf-8")
    Assert(len(cmd) and cmd.startswith("curl"), "命令行不对")
    parsed_context = curl_parse_context(cmd)
    params = {
        "data":
        parsed_context.data,
        "headers":
        dict(
            filter(lambda x: not x[0].startswith(":"),
                   parsed_context.headers.items())),
        "cookies":
        parsed_context.cookies,
    }
    if parsed_context.method == 'get':
        rsp = requests.get(
            parsed_context.url,
            **params,
        )
    else:
        rsp = requests.post(
            parsed_context.url,
            **params,
        )
    if rsp.status_code != 200:
        return {
            "succ": False,
            "reason": "无效的curl",
        }
    if parsed_context.url != "https://developer.apple.com/services-account/QH65B2/account/getUserProfile":
        data = rsp.json()
        account = data["userProfile"]["email"]
    else:
        rsp = requests.post(
            "https://developer.apple.com/services-account/QH65B2/account/getUserProfile",
            headers={
                "cookie": parsed_context.cookies,
            })
        data = rsp.json()
        account = data["userProfile"]["email"]

    if account:
        _info = IosAccountInfo.objects.filter(
            account=account).first()  # type:IosAccountInfo
        if not _info:
            _info = IosAccountInfo()
            _info.account = account
            _info.save()
        _info.cookie = json_str(parsed_context.cookies)
        _info.headers = json_str(parsed_context.headers)
        _info.save()
        Log("通过curl登录[%s]" % account)
        return {
            "succ": True,
            "msg": "登录[%s]成功" % account,
        }
    else:
        return {
            "succ": False,
            "msg": "请求不具备提取登录信息",
        }
Exemple #16
0
 def __init__(self, key: str, timeout=None):
     Assert(key, "key必须有效")
     self.__key = "lock:" + key
     self.__lock = False
     self.__timeout = timeout
Exemple #17
0
    def post(self,
             title: str,
             url: str,
             data: Union[Dict, str] = None,
             is_json=True,
             log=True,
             cache: Union[bool, int] = False,
             csrf=False,
             json_api=True,
             method="POST",
             is_binary=False,
             ex_headers=None,
             status=200):
        if cache is True:
            expire = 3600 * 1000
        else:
            expire = cache

        if not self.is_login:
            self.__login()
        start = now()
        rsp_str = "#NODATA#"
        try:
            if "teamId=" in url:
                if "teamId=%s" % self.team_id not in url:
                    url = url.replace("teamId=", "teamId=%s" % self.team_id)
            if cache:
                rsp_str = _cache(url, data) or rsp_str
            headers = {
                'cookie':
                to_form_url({"myacinfo": self.cookie["myacinfo"]}, split=';'),
            }
            if csrf:
                headers.update({
                    'csrf': self.csrf,
                    'csrf_ts': self.csrf_ts,
                })
            if ex_headers:
                headers.update(ex_headers)
            if rsp_str == "#NODATA#":
                if method.upper() == "GET":
                    rsp = requests.get(url,
                                       params=data,
                                       headers=headers,
                                       timeout=3,
                                       verify=False)
                else:
                    rsp = requests.post(url,
                                        data=data,
                                        headers=headers,
                                        timeout=3,
                                        verify=False)

                rsp_str = rsp.text
                if rsp.headers.get("csrf"):
                    self.csrf = rsp.headers["csrf"]
                    self.csrf_ts = int(rsp.headers["csrf_ts"])
                Assert(rsp.status_code == status,
                       "请求[%s]异常[%s]" % (title, rsp.status_code))
                if json_api:
                    _data = str_json_i(rsp_str) or {}
                    if _data.get("resultCode") == 1100:
                        self.__logout()
                        raise Fail("登录[%s]过期了[%s][%s]" %
                                   (self.account, _data.get("resultString"),
                                    _data.get("userString")))
                    Assert(
                        _data.get("resultCode") == 0, "请求业务[%s]失败[%s][%s]" %
                        (title, _data.get("resultString"),
                         _data.get("userString")))
                if log:
                    Log("apple请求[%s][%s]发送[%r]成功[%r]" %
                        (title, now() - start, data, rsp_str))
                if is_binary:
                    rsp_str = base64(rsp.content)
                if cache:
                    _set_cache(url, data, rsp_str, expire=expire)
            if is_json:
                return str_json(rsp_str)
            elif is_binary:
                return base64decode(rsp_str)
            else:
                return rsp_str
        except Exception as e:
            if log:
                Log("apple请求[%s][%s]发送[%r]失败[%r]" %
                    (title, now() - start, data, rsp_str))
            raise e
Exemple #18
0
    def __login(self):
        if self.csrf_ts > now():
            return
        with Block("账号登录"):
            ret = requests.post(
                "https://developer.apple.com/services-account/QH65B2/account/getTeams",
                json={
                    "includeInMigrationTeams": 1,
                },
                headers={
                    'cookie': to_form_url(self.cookie, split=';'),
                },
                timeout=3,
                verify=False).json() if not self.team_id else {}
            if ret.get("resultCode") == 0:
                self.teams = list(map(lambda x: x["teamId"], ret["teams"]))
                self.info.team_id = self.team_id = self.teams[0]
                self.info.teams = json_str(self.teams)
                self.info.save()
            else:
                # 重新登录
                self.session = requests.session()
                self.session.headers["User-Agent"] = "Spaceship 2.117.1"

                rsp = self.session.get(
                    "https://olympus.itunes.apple.com/v1/app/config?hostname=itunesconnect.apple.com"
                ).json()
                self.session.headers["X-Apple-Widget-Key"] = rsp[
                    "authServiceKey"]
                # self.session.headers["X-Apple-Widget-Key"] = "16452abf721961a1728885bef033f28e"
                self.session.headers["Accept"] = "application/json"
                rsp = self.session.post(
                    "https://idmsa.apple.com/appleauth/auth/signin",
                    json={
                        "accountName": self.account,
                        "password": self.password,
                        "rememberMe": True,
                    },
                    timeout=3)
                self.session.headers["x-apple-id-session-id"] = rsp.headers[
                    "x-apple-id-session-id"]
                self.session.headers["scnt"] = rsp.headers["scnt"]
                if rsp.status_code == 409:
                    # 二次验证
                    # noinspection PyUnusedLocal
                    rsp = self.session.post(
                        "https://idmsa.apple.com/appleauth/auth")
                    # Log("===> https://idmsa.apple.com/appleauth/auth [%s] %s" % (rsp.status_code, rsp.json()))

                    # 切手机验证码
                    rsp = self.session.put(
                        "https://idmsa.apple.com/appleauth/auth/verify/phone",
                        json={
                            "phoneNumber": {
                                "id": 1
                            },
                            "mode": "sms",
                        })
                    Assert(rsp.status_code == 200, "[%s]短信发送失败" % self.account)
                    # Log("===> https://idmsa.apple.com/appleauth/auth/verify/phone [%s] %s" % (rsp.status_code, rsp.json()))
                    _wait_code(self.info, self.session, now())

                self.cookie.update(self.session.cookies)
                self.__expire = now() + 3600 * 1000
        if not self.team_id:
            ret = requests.post(
                "https://developer.apple.com/services-account/QH65B2/account/getTeams",
                json={
                    "includeInMigrationTeams": 1,
                },
                headers={
                    'cookie': to_form_url(self.cookie, split=';'),
                },
                timeout=3,
                verify=False).json()
            if ret["resultCode"] == 0:
                self.teams = list(map(lambda x: x["teamId"], ret["teams"]))
                self.info.team_id = self.team_id = self.teams[0]
                self.info.teams = json_str(self.teams)
                self.info.save()
            else:
                Log("[%s]获取team失败登出了" % self.account)
                self.__logout()
        Log("apple账号[%s:%s]登录完成了" % (self.account, self.team_id))
Exemple #19
0
def __add_device(account: IosAccountInfo, udid: str, project: str) -> bool:
    title = "设备%s" % udid
    _config = IosAccountHelper(account)
    try:
        _device = IosDeviceInfo.objects.filter(
            udid=udid).first()  # type:Optional[IosDeviceInfo]
        if not _device:
            # 先注册设备
            ret = _config.post(
                "验证设备udid",
                "https://developer.apple.com/services-account/QH65B2/account/ios/device/validateDevices.action?teamId=",
                {
                    "deviceNames": title,
                    "deviceNumbers": udid,
                    "register": "single",
                    "teamId": _config.team_id,
                },
                cache=True)

            Assert(
                len(ret["failedDevices"]) == 0,
                "验证udid请求失败[%s][%s]" % (udid, ret["validationMessages"]))
            __list_all_devices(_config)
            ret = _config.post(
                "添加设备",
                "https://developer.apple.com/services-account/QH65B2/account/ios/device/addDevices.action?teamId=%s"
                % _config.team_id, {
                    "deviceClasses": "iphone",
                    "deviceNames": title,
                    "deviceNumbers": udid,
                    "register": "single",
                    "teamId": _config.team_id,
                },
                csrf=True)
            Assert(ret["resultCode"] == 0, "添加udid请求失败[%s]" % udid)
            Assert(not ret["validationMessages"], "添加udid请求失败[%s]" % udid)
            Assert(ret["devices"], "添加udid请求失败[%s]" % udid)
            device = ret["devices"][0]  # type: Dict
            _reg_device(device["deviceId"], device["deviceNumber"],
                        device["model"], device["serialNumber"])

        with Block("更新"):
            ret, _info = __list_all_profile(_config, project)
            if not _info:
                _info = IosProfileInfo()
                _info.sid = "%s:%s" % (_config.account, project)
                _info.app = _config.account
                _info.devices = "[]"
                _info.devices_num = 0
                _info.project = project

            devices = str_json(_info.devices)  # type: List[str]

            if udid in devices:
                pass
            else:
                devices.append(udid)
                with Block("默认全开当期的设备"):
                    # noinspection PyTypeChecker
                    devices = list(
                        set(devices + str_json(_config.info.devices)))
                _cert = _get_cert(_config.info)
                _app = _get_app(_config, project)
                found = False
                for each in ret["provisioningProfiles"]:  # type: Dict
                    if each["name"] != "专用 %s" % project:
                        continue
                    # todo: 过期更新
                    ret = _config.post(
                        "更新ProvisioningProfile",
                        "https://developer.apple.com/services-account/QH65B2/account/ios/profile/regenProvisioningProfile.action?teamId=",
                        data={
                            "provisioningProfileId":
                            each["provisioningProfileId"],
                            "distributionType":
                            "limited",
                            "subPlatform":
                            "",
                            "returnFullObjects":
                            False,
                            "provisioningProfileName":
                            each["name"],
                            "appIdId":
                            _app.app_id_id,
                            "certificateIds":
                            _cert.cert_req_id,
                            "deviceIds":
                            ",".join(_get_device_id(devices).values()),
                        },
                        csrf=True)
                    Assert(ret["resultCode"] == 0)
                    _info.devices = json_str(devices)
                    _info.profile_id = each["provisioningProfileId"]
                    # noinspection PyTypeChecker
                    _info.profile = ret["provisioningProfile"][
                        "encodedProfile"]
                    _info.expire = _to_dt(
                        ret["provisioningProfile"]["dateExpire"])
                    _info.save()
                    found = True
                    Log("更新证书[%s]添加设备[%s][%s]成功" %
                        (project, udid, len(devices)))
                    break
                if not found:
                    ret = _config.post(
                        "创建ProvisioningProfile",
                        "https://developer.apple.com/services-account/QH65B2/account/ios/profile/createProvisioningProfile.action?teamId=",
                        data={
                            "subPlatform": "",
                            "certificateIds": _cert.cert_req_id,
                            "deviceIds":
                            ",".join(_get_device_id(devices).values()),
                            "template": "",
                            "returnFullObjects": False,
                            "distributionTypeLabel": "distributionTypeLabel",
                            "distributionType": "limited",
                            "appIdId": _app.app_id_id,
                            "appIdName": _app.name,
                            "appIdPrefix": _app.prefix,
                            "appIdIdentifier": _app.identifier,
                            "provisioningProfileName": "专用 %s" % project,
                        },
                        csrf=True)
                    Assert(ret["resultCode"] == 0)
                    # noinspection PyTypeChecker
                    _info.profile_id = ret["provisioningProfile"][
                        "provisioningProfileId"]
                    # noinspection PyTypeChecker
                    _info.profile = ret["provisioningProfile"][
                        "encodedProfile"]
                    _info.expire = _to_dt(
                        ret["provisioningProfile"]["dateExpire"])
                    _info.save()
                    Log("添加证书[%s]添加设备[%s][%s]成功" %
                        (project, udid, len(devices)))
    except Exception as e:
        Trace("添加设备出错了[%s]" % e, e)
        return False
    return True