Exemplo n.º 1
0
 def __init__(self, info: IosAccountInfo):
     self.info = info
     self.account = info.account
     self.password = info.password
     self.teams = str_json_i(info.teams, default=[])
     self.team_id = info.team_id
     self.headers = str_json(info.headers)
     self.cookie = str_json(info.cookie)  # type: Dict[str,str]
     self.csrf = info.csrf
     self.csrf_ts = info.csrf_ts
     self.session = requests.session()
Exemplo n.º 2
0
def _reg_cert(_config: IosAccountInfo, cert_req_id, name, cert_id, sn,
              type_str, expire):
    sid = "%s:%s" % (_config.account, name)
    orig = str_json(
        db_model.get("IosCertInfo:%s:%s" % (_config.account, name)) or '{}')
    obj = {
        "name": name,
        "app": _config.account,
        "cert_req_id": cert_req_id,
        "cert_id": cert_id,
        "sn": sn,
        "type_str": type_str,
        "expire": expire,
        "expire_str": date_time_str(expire),
    }
    if orig == obj:
        return cert_req_id
    db_model.set("IosCertInfo:%s:%s" % (_config.account, name),
                 json_str(obj),
                 ex=(expire - now()) // 1000)
    _info = IosCertInfo()
    _info.sid = sid
    _info.app = _config.account
    _info.cert_req_id = cert_req_id
    _info.cert_id = cert_id
    _info.sn = sn
    _info.type_str = type_str
    _info.name = name
    _info.create = now()
    _info.expire = datetime.datetime.utcfromtimestamp(expire // 1000)
    _info.save()
    Log("注册新的证书[%s][%s]" % (name, cert_req_id))
    return cert_req_id
Exemplo n.º 3
0
def __list_all_devices(_config: IosAccountHelper):
    ret = _config.post(
        "获取所有的列表",
        "https://developer.apple.com/services-account/QH65B2/account/ios/device/listDevices.action?teamId=",
        data={
            "includeRemovedDevices": True,
            "includeAvailability": True,
            "pageNumber": 1,
            "pageSize": 100,
            "sort": "status%3dasc",
            "teamId": _config.team_id,
        },
        log=False)
    for device_id in set(map(lambda x: x["deviceId"], ret["devices"])) - set(
            str_json(_config.info.devices).values()):  # type: Dict
        device = list(
            filter(lambda x: x["deviceId"] == device_id, ret["devices"]))[0]
        _reg_device(
            _config.account, device["deviceId"], device["deviceNumber"],
            device.get("model", device.get("deviceClass", "#UNKNOWN#")),
            device.get("serialNumber", "#UNKNOWN#"))
    # 更新一下info
    devices = dict(
        map(lambda x: (x["deviceNumber"], x["deviceId"]), ret["devices"]))
    if json_str(devices) != _config.info.devices:
        Log("更新设备列表[%s]数量[%s]=>[%s]" %
            (_config.account, _config.info.devices_num, len(devices)))
        _config.info.devices = json_str(devices)
        _config.info.devices_num = len(devices)
        _config.info.save()
Exemplo n.º 4
0
def mongo_pack_get(key: str, model: str, pop=False) -> Optional[Dict]:
    ret = _mongo_pack_get(key, model, pop=pop)
    if len(ret) == 0:
        return None
    if len(ret) == 1:
        return ret[0]
    return str_json("".join(map(lambda x: x["__value__"], ret)))
Exemplo n.º 5
0
def _get_device_id(udid_list: List[str]) -> Dict[str, str]:
    return dict(
        zip(
            udid_list,
            map(
                lambda x: str_json(x)["device_id"] if x else x,
                db_model.mget(
                    list(map(lambda x: "IosDeviceInfo:%s" % x, udid_list))))))
Exemplo n.º 6
0
def info(_req: HttpRequest, project: str, uuid: str = ""):
    _project = IosProjectInfo.objects.filter(
        project=project).first()  # type: IosProjectInfo
    udid = ""
    if not _project:
        return {}
    ret = str_json(_project.comments)
    ready = False

    if uuid:
        _user = UserInfo.objects.filter(uuid=uuid).first()  # type: UserInfo
        if _user:
            ready = True
            udid = _user.udid
        else:
            Log("上传的uuid无效[%s]" % uuid)
    else:
        uuid = _req.get_signed_cookie("uuid", "", salt="zhihu")
        _user = UserInfo.objects.filter(uuid=uuid).first()  # type: UserInfo
        if _user:
            ready = True
            udid = _user.udid
        else:
            Log("cookie中的uuid无效[%s]" % uuid)

    if ready:
        ret.update({
            "ready": True,
        })
        _task = TaskInfo.objects.filter(uuid=uuid).first()  # type:TaskInfo
        if _task:
            if _task.state == "fail" or _task.expire.timestamp() * 1000 < now(
            ):
                __add_task(_user)

    else:
        uuid = _newbee(_project)
        ret.update({
            "ready": False,
        })

    ret.update({
        "uuid": uuid,
    })
    rsp = JsonResponse({
        "ret": 0,
        "result": ret,
        "download_id": random_str(32),
    })
    rsp.set_signed_cookie("uuid", uuid, salt="zhihu", expires=3600 * 24)
    if udid:
        rsp.set_signed_cookie("udid",
                              _user.udid,
                              salt="zhihu",
                              expires=300 * 3600 * 24)
    return rsp
Exemplo n.º 7
0
def info(project: str):
    _project = IosProjectInfo.objects.filter(
        project=project).first()  # type: IosProjectInfo
    if _project:
        ret = str_json(_project.comments)
        ret.update({
            "uuid": _newbee(_project),
        })
        return ret
    return {}
Exemplo n.º 8
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))
Exemplo n.º 9
0
def add_device(_content: bytes, uuid: str, udid: str = ""):
    _key = "uuid:%s" % uuid
    _detail = str_json(db_session.get(_key) or "{}")
    _account = _detail.get("account")
    project = _detail["project"]
    if not _detail:
        raise Fail("无效的uuid[%s]" % uuid)
    if not udid:
        # todo: 验证来源
        with Block("提取udid", fail=False):
            udid = re.compile(br"<key>UDID</key>\n\s+<string>(\w+)</string>"
                              ).findall(_content)[0].decode("utf8")
    if not udid:
        # 提取udid失败后删除uuid
        db_session.delete(_key)
        return {
            "succ": False,
        }
    for _user in UserInfo.objects.filter(udid=udid):
        _account = _user.account
        if _user.project == _detail["project"]:
            if uuid != _user.uuid:
                Log("转移设备的[%s]的uuid[%s]=>[%s]" % (udid, uuid, _user.uuid))
                uuid = _user.uuid
                break

    if not _account:
        Log("为设备[%s]分配账号" % udid)
        _account = __fetch_account(udid, project, __add_device)
    else:
        _account = IosAccountInfo.objects.filter(account=_account).first()

    _user = UserInfo(uuid=uuid)
    _user.udid = udid
    _user.project = _detail["project"]
    _user.account = _account.account
    _user.save()
    if test_env():
        # 测试环境可能uuid要重复测
        pass
    else:
        db_session.delete(_key)
    __add_task(_user)
    return HttpResponsePermanentRedirect(
        "https://iosstore.sklxsj.com/detail.php?project=%s" % project)
Exemplo n.º 10
0
def _reg_device(account: str, device_id: str, udid: str, model: str,
                sn: str) -> str:
    # 需要缓存
    _info = IosDeviceInfo()
    _info.sid = "%s:%s" % (account, udid)
    _info.account = account
    _info.udid = udid
    _info.device_id = device_id
    _info.model = model
    _info.sn = sn
    _info.create = now()
    _info.save()
    Log("注册新的设备[%s][%s][%s]" % (udid, device_id, sn))
    _account = IosAccountInfo.objects.get(account=account)
    device_map = str_json(_account.devices)
    device_map[udid] = device_id
    if json_str(device_map) != _account.devices:
        _account.devices = json_str(device_map)
        _account.devices_num = len(device_map)
        _account.save()
    return udid
Exemplo n.º 11
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
Exemplo n.º 12
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
Exemplo n.º 13
0
def manifest(uuid: str, need_process=True, download_id: str = ""):
    _user = UserInfo.objects.get(uuid=uuid)
    _account = IosAccountInfo.objects.get(account=_user.account)
    _project = IosProjectInfo.objects.get(project=_user.project)
    _app = IosAppInfo.objects.get(sid="%s:%s" % (_user.account, _user.project))
    _comments = str_json(_project.comments)
    content = """\
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>items</key>
        <array>
            <dict>
                <key>assets</key>
                <array>
                    <dict>
                        <key>kind</key>
                        <string>software-package</string>
                        <key>url</key>
                        <string>%(url)s</string>
                    </dict>
                    <dict>
                        <key>kind</key>
                        <string>display-image</string>
                        <key>url</key>
                        <string>%(icon)s</string>
                    </dict>
                    <dict>
                        <key>kind</key>
                        <string>full-size-image</string>
                        <key>url</key>
                        <string>%(icon)s</string>
                    </dict>
                </array>
                <key>metadata</key>
                <dict>
                    <key>bundle-identifier</key>
                    <string>%(app)s</string>
                    <key>bundle-version</key>
                    <string>%(version)s</string>
                    <key>kind</key>
                    <string>software</string>
                    <key>title</key>
                    <string>%(title)s</string>
                </dict>
            </dict>
        </array>
    </dict>
</plist>
""" % {
        "url":
        static_entry("/income/%s/%s_%s.ipa") %
        (_project.project, _account.team_id, _account.devices_num)
        if not need_process else entry(
            "/apple/download_ipa/uuid/%s/download_id/%s" %
            (uuid, download_id or random_str())),
        "uuid":
        uuid,
        "title":
        _comments["name"],
        "icon":
        _comments["icon"],
        "app":
        _app.identifier,
        "version":
        "1.0.0",
    }
    response = HttpResponse(content)
    response['Content-Type'] = 'application/octet-stream; charset=utf-8'
    response["Content-Disposition"] = 'attachment; filename="app.plist"'

    return response
Exemplo n.º 14
0
def validate_json(value: str):
    try:
        str_json(value)
    except ValueError:
        raise ValidationError(_('不合法的json字符串'), code='invalid')