def store_token(self, data): Core.debug("storing OAuth token for %r", self.domain) self._store_token_libsecret(data) try: self._store_token_file(data) except Exception as e: Core.warn("could not write %r: %r", self.token_path, e)
def _forget_token(self): Core.debug("flushing OAuth tokens") nullroute.sec.clear_libsecret({ "xdg:schema": self.TOKEN_SCHEMA, "domain": "pixiv.net" }) os.unlink(self.TOKEN_PATH)
def increment_attr(self, dn, attr, incr=1, use_increment=True): import random import time if use_increment and \ self.has_control(OID_LDAP_CONTROL_POSTREAD) and \ self.has_feature(OID_LDAP_FEATURE_MODIFY_INCREMENT): incr = str(incr).encode() ctrl = ldap.controls.readentry.PostReadControl(attrList=[attr]) res = self.conn.modify_ext_s(dn, [(ldap.MOD_INCREMENT, attr, incr)], serverctrls=[ctrl]) for outctrl in res[3]: if outctrl.controlType == ctrl.controlType: values = CaseInsensitiveDict(outctrl.entry)[attr] return int(values[0]) wait = 0 while True: old_val = self.read_attr(dn, attr, raw=True)[0] new_val = str(int(old_val) + incr).encode() try: self.conn.modify_s(dn, [(ldap.MOD_DELETE, attr, old_val), (ldap.MOD_ADD, attr, new_val)]) done = True except ldap.NO_SUCH_ATTRIBUTE as e: Core.debug("swap (%r, %r) failed: %r", old_val, new_val, e) wait += 1 time.sleep(0.05 * 2**random.randint(0, wait)) else: break return int(new_val)
def _discover_endpoints(self): if not self.discovery_url: raise ValueError( "either discovery URL or endpoint URLs must be specified") Core.debug("fetching discovery document %r", self.discovery_url) response = urllib.request.urlopen(self.discovery_url).read() response = json.loads(response) Core.debug("response data: %r", response) if not self.authorization_url: self.authorization_url = response["authorization_endpoint"] if not self.token_grant_url: self.token_grant_url = response["token_endpoint"]
def load_token(self): Core.debug("loading OAuth token for %r", self.domain) try: return self._load_token_libsecret() except KeyError: try: return self._load_token_file() except FileNotFoundError: pass except Exception as e: Core.debug("could not load %r: %r", self.token_path, e) self.forget_token() return None
def _load_member_name_map(self): map_path = Env.find_config_file("pixiv_member_names.txt") try: Core.debug("loading member aliases from %r", map_path) with open(map_path, "r") as fh: self.member_name_map = {} for line in fh: if line.startswith((";", "#", "\n")): continue k, v = line.split("=") self.member_name_map[k.strip()] = v.strip() except FileNotFoundError: Core.debug("member alias file %r not found; ignoring", map_path)
def request_tls_certificate(self, domains, csr, years=3): Core.debug("posting a ssl_multi_domain order for %r", domains) org = self.get_organizations()[0] order = { "certificate": { "common_name": domains[0], "csr": csr, "dns_names": domains, "signature_hash": "sha256", }, "organization": { "id": org["id"] }, "validity_years": years, } data = self.post_order("ssl_multi_domain", order) return data
def __init__(self, url, require_tls=True): Core.debug("creating libldap connection to %r", url) self.conn = ldap.initialize(url) if require_tls and not url.startswith(("ldaps://", "ldapi://")): self.conn.start_tls_s() self.rootDSE = CaseInsensitiveDict(self.conn.read_rootdse_s()) self._controls = { v.decode() for v in self.rootDSE.get("supportedControl", []) } self._features = { v.decode() for v in self.rootDSE.get("supportedFeatures", []) }
def _load_token_libsecret(self): if self.user_name: try: data = nullroute.sec.get_libsecret(self.match_fields) return json.loads(data) except KeyError: Core.debug("entry not found; retrying without username") old_match_fields = {**self.match_fields} del old_match_fields["username"] data = nullroute.sec.get_libsecret(old_match_fields) Core.debug("migrating entry to add username field") nullroute.sec.clear_libsecret(old_match_fields) self._store_token_libsecret(json.loads(data)) return json.loads(data) else: data = nullroute.sec.get_libsecret(self.match_fields) return json.loads(data)
def _authenticate(self): if self.api.user_id: Core.warn("BUG: _authenticate() called twice") return True data = self._load_token() if data: Core.trace("loaded token: %r", data) if os.environ.get("FORCE_TOKEN_REFRESH"): token_valid = False else: token_valid = data["expires_at"] > time.time() if token_valid: Core.debug("access token within expiry time, using as-is") self.api.user_id = data["user_id"] self.api.access_token = data["access_token"] self.api.refresh_token = data["refresh_token"] return True else: Core.debug("access token has expired, renewing") try: token = self.api.auth(refresh_token=data["refresh_token"]) Core.trace("retrieved token: %r", token) except Exception as e: Core.warn("could not refresh access token: %r", e) #self._forget_token() else: self._store_token(token) return True data = self._load_creds() if data: Core.info("logging in to Pixiv as %r", data["login"]) try: token = self.api.auth(username=data["login"], password=data["password"]) except Exception as e: Core.warn("could not log in using username & password: %r", e) else: self._store_token(token) return True Core.die("could not log in to Pixiv (no credentials)") return False
def _store_token(self, token): data = { "access_token": token.response.access_token, "refresh_token": token.response.refresh_token, "expires_at": int(time.time() + token.response.expires_in), "user_id": token.response.user.id, } Core.debug("storing OAuth tokens") nullroute.sec.store_libsecret("Pixiv OAuth token", json.dumps(data), {"xdg:schema": self.TOKEN_SCHEMA, "domain": "pixiv.net", "userid": token.response.user.id, "username": token.response.user.account}) try: with open(self.TOKEN_PATH, "w") as fh: json.dump(data, fh) return True except Exception as e: Core.warn("could not write %r: %r", self.TOKEN_PATH, e) return False
def _validate(self): Core.debug("verifying session status") resp = self.get("https://www.pixiv.net/member.php", allow_redirects=False) if resp.is_redirect: Core.trace("member.php redirects to %r", resp.next.url) url = requests.utils.urlparse(resp.next.url) if url.path == "/member.php": query = parse_query_string(url.query) self.user_id = int(query["id"]) Core.debug("session is valid, userid %r", self.user_id) return True Core.debug("session is not valid") return False
def _authenticate(self): if self.user_id: return True psid = os.environ.get("PIXIV_PHPSESSID") if psid: cookie = requests.cookies.create_cookie(name="PHPSESSID", value=psid, domain=".pixiv.net") cookie.expires = int(time.time() + 3600) Core.debug("storing cookie: %r", cookie) self._store_token(serialize_cookie(cookie)) token = self._load_token() if token: if os.environ.get("FORCE_TOKEN_REFRESH"): del os.environ["FORCE_TOKEN_REFRESH"] token_valid = False else: #token_valid = token["expires"] >= time.time() # Just pretend the cookie is still valid, as it now comes # from the web browser which will keep it active, and we # don't really have any way to get a new one anyway. token_valid = True token["expires"] = int(time.time() + 86400 * 30) if token_valid: cookie = requests.cookies.create_cookie(**token) Core.debug("loaded cookie: %r", cookie) self.ua.cookies.set_cookie(cookie) if self._validate(): cookie.expires = int(time.time() + 86400 * 30) Core.debug("updating cookie: %r", cookie) self._store_token(serialize_cookie(cookie)) return True else: Core.debug("cookie has expired") raise Exception("Pixiv cookie not found or expired")
def _load_token(self): try: data = nullroute.sec.get_libsecret({"xdg:schema": self.TOKEN_SCHEMA, "domain": "pixiv.net"}) Core.debug("found OAuth token in keyring") return json.loads(data) except KeyError: try: with open(self.TOKEN_PATH, "r") as fh: data = json.load(fh) Core.debug("found OAuth token in filesystem") return data except FileNotFoundError: pass except Exception as e: Core.debug("could not load %r: %r", self.TOKEN_PATH, e) self._forget_token() return None
def _grant_token(self, params): if not self.token_grant_url: self._discover_endpoints() Core.debug("token grant URL: %r", self.token_grant_url) post_data = { "client_id": self.client_id, "client_secret": self.client_secret, **params } Core.debug("request data: %r", post_data) post_data = urllib.parse.urlencode(post_data).encode() Core.debug("encoded data: %r", post_data) response = urllib.request.urlopen(self.token_grant_url, post_data).read() response = json.loads(response) response.setdefault("expires_at", int(time.time() + response["expires_in"])) return response
def post(self, ep, *args, **kwargs): uri = self.base + ep Core.debug("posting to %r" % uri) resp = self.ua.post(uri, *args, **kwargs) resp.raise_for_status() return resp
def get(self, ep, *args, **kwargs): uri = self.base + ep Core.debug("fetching %r" % uri) resp = self.ua.get(uri, *args, **kwargs) resp.raise_for_status() return resp
def forget_token(self): Core.debug("flushing OAuth tokens for %r", self.domain) self._clear_token_libsecret() self._clear_token_file()
sshsigbuf = sys.stdin.read() namespace = args.namespace or "" sshsig_outer = SshsigSignature.from_armored(sshsigbuf) keyblob = sshsig_outer.public_key sigblob = sshsig_outer.signature keydata = ssh_parse_publickey(keyblob) sigdata = ssh_parse_signature(sigblob) if args.publickey: agentkey = SshAgentKey(None, keyblob) if args.publickey == agentkey.publickey_base64(): Core.debug("enveloped publickey matches provided") elif args.publickey == agentkey.publickey_base64(with_type=True): Core.debug("enveloped publickey matches provided") else: Core.die("enveloped publickey does not match provided") if args.fingerprint: agentkey = SshAgentKey(None, keyblob) if args.fingerprint == agentkey.fprint_md5_hex(): Core.debug( "enveloped publickey MD5 fingerprint matches provided one") elif args.fingerprint == agentkey.fprint_sha256_base64(): Core.debug( "enveloped publickey SHA256 fingerprint matches provided one") else: Core.die("enveloped publickey fingerprint does not match provided") if args.namespace: