From 378508196dfcda2882105611c9b8f4f88ac89d16 Mon Sep 17 00:00:00 2001 From: Franck Awounang Date: Mon, 24 Jul 2017 04:50:41 +0200 Subject: [PATCH 1/5] Added support for api-version selection. --- hibp/hibp.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/hibp/hibp.py b/hibp/hibp.py index 0454bcb..781aa5f 100644 --- a/hibp/hibp.py +++ b/hibp/hibp.py @@ -16,8 +16,9 @@ monkey.patch_all(thread=False, select=False) # global variables -BASE_URL = "https://haveibeenpwned.com/api/v2/" +BASE_URL = "https://haveibeenpwned.com/api/{api_version}/" HEADERS = {"User-Agent": "hibp-python",} +DEFAULT_API_VERSION = "v2" # enumerate the types of services that are callable class Services(Enum): @@ -45,30 +46,33 @@ def __init__(self): self.response = None @classmethod - def get_account_breaches(cls,account): + def get_account_breaches(cls,account, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all breaches on a particular account Args: - account -> account you want to query. can be email or username to anything + - api_version -> the server's requested api version: e.g v1 or v2 Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "breachedaccount/{}".format(account) + req.url = BASE_URL.format(api_version=api_version) + \ + "breachedaccount/{}".format(account) req.service = Services.AccountBreach req.param = account return req @classmethod - def get_domain_breaches(cls,domain): + def get_domain_breaches(cls,domain, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all breaches on a particular domain Args: - domain -> domain you want to query. must be valid domain, according to RFC 1035 + - api_version -> the server's requested api version: e.g v1 or v2 Returns: - HIBP object with updated url, service, and param attributes ''' @@ -76,51 +80,60 @@ def get_domain_breaches(cls,domain): domain_regex = re.compile(r"[a-zA-Z\d-]{,63}(\.[a-zA-Z\d-]{,63})+") if not re.match(domain_regex, domain): raise ValueError("{} is an invalid domain.".format(domain)) - req.url = BASE_URL + "breaches?domain={}".format(domain) + req.url = BASE_URL.format(api_version=api_version) + \ + "breaches?domain={}".format(domain) req.service = Services.DomainBreach req.param = domain return req @classmethod - def get_breach(cls,name): + def get_breach(cls,name, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve a specific breach. Args: - name -> name of breach you want to query. To get a list of all breach names, run HIBP.get_all_breaches() + - api_version -> the server's requested api version: e.g v1 or v2 Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "breach/{}".format(name) + req.url = BASE_URL.format(api_version=api_version) + \ + "breach/{}".format(name) req.service = Services.Breach req.param = name return req @classmethod - def get_all_breaches(cls): + def get_all_breaches(cls, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all breaches recorded on HIBP.com so far. + Args: + - api_version -> the server's requested api version: e.g v1 or v2 + Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "breaches" + req.url = BASE_URL.format(api_version=api_version) + "breaches" req.service = Services.AllBreaches return req @classmethod - def get_dataclasses(cls): + def get_dataclasses(cls, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all dataclasses on HIBP. + Args: + - api_version -> the server's requested api version: e.g v1 or v2 + Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "dataclasses" + req.url = BASE_URL.format(api_version=api_version) + "dataclasses" req.service = Services.DataClasses return req From cee29eae4dcb9415b1754e712816bb3e9124fa4d Mon Sep 17 00:00:00 2001 From: Franck Awounang Date: Mon, 24 Jul 2017 06:14:48 +0200 Subject: [PATCH 2/5] Added api's min interval call and switched concurrency to serial processing to avoid exceeding rate-limit on server. see: https://haveibeenpwned.com/API/v2#RateLimiting . --- hibp/example.py | 2 +- hibp/hibp.py | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/hibp/example.py b/hibp/example.py index 5cc7433..1cb104b 100644 --- a/hibp/example.py +++ b/hibp/example.py @@ -19,7 +19,7 @@ ### SERIAL start_time = time.time() for req in reqs: - req.execute() + req.execute_min_delay() elapsed_time = time.time() - start_time logging.info("serial impl took %.2f seconds" % elapsed_time) diff --git a/hibp/hibp.py b/hibp/hibp.py index 781aa5f..cb9893c 100644 --- a/hibp/hibp.py +++ b/hibp/hibp.py @@ -1,5 +1,6 @@ from enum import Enum import re +import time try: import requests @@ -19,6 +20,23 @@ BASE_URL = "https://haveibeenpwned.com/api/{api_version}/" HEADERS = {"User-Agent": "hibp-python",} DEFAULT_API_VERSION = "v2" +#API delay between two calls +API_CALL_DELAY = 1.601 + +# decorator for api min-delay calls +def api_min_delay(base_func): + '''Forces the called function to wait until the api's call delay + is ellapsed before returning, to avoid exceeding rate.''' + def func(*args, **kwargs): + start = time.time() + res = base_func(*args, **kwargs) + stop = time.time() + ellapsed_time = stop-start + remaining_time = max(API_CALL_DELAY - ellapsed_time, 0) + time.sleep(remaining_time) + return res + return func + # enumerate the types of services that are callable class Services(Enum): @@ -141,6 +159,8 @@ def execute(self): ''' Execute a GET request on HIBP REST API service based on request object setup with one of the query services above. + If many queries are to be executed in batch, use @execute_min_delay + instead. Returns: - If query parameter is pwned: @@ -171,6 +191,13 @@ def execute(self): else: self.response = response.json() return self + + def execute_min_delay(self): + '''Calls execute and make sure the minimal delay between two api calls + is ellapsed before returning.''' + delayed_func = api_min_delay(self.execute) + return delayed_func() + class AsyncHIBP(object): ''' @@ -193,6 +220,7 @@ def __init__(self): self.url = None self.response = None + @api_min_delay def send(self,hibp_obj): ''' Spawns gevent/pool threads that will run the execute method on each @@ -223,6 +251,5 @@ def imap(self,hibp_objs): Attributes: - hibp_objs - list of HIBP objects ''' - for hibp_obj in self.pool.imap_unordered(HIBP.execute, hibp_objs): - yield hibp_obj.response - self.pool.join() + for hibp_obj in hibp_objs: + yield HIBP.execute_min_delay(hibp_obj) From cb413eb5c7d65afed06822a1e96e10fe8cb5ecae Mon Sep 17 00:00:00 2001 From: Franck Awounang Date: Mon, 24 Jul 2017 06:28:02 +0200 Subject: [PATCH 3/5] Added support for paste-accounts and a corresponding example. --- hibp/example.py | 4 +++- hibp/hibp.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/hibp/example.py b/hibp/example.py index 1cb104b..6dba2fe 100644 --- a/hibp/example.py +++ b/hibp/example.py @@ -10,11 +10,13 @@ names = ['adobe','ashleymadison', 'naughtyamerica', 'myspace'] accounts = ["ssgrn", "pegasos1","barackobama"] domains = ['twitter.com', 'facebook.com','github.com','adobe.com'] + paste_accounts = ['test@example.com'] # setup HIBP objects for request executions reqs = [HIBP.get_breach(x) for x in names] \ + [HIBP.get_account_breaches(x) for x in accounts] \ - + [HIBP.get_domain_breaches(x) for x in domains] + + [HIBP.get_domain_breaches(x) for x in domains] \ + + [HIBP.get_paste_account(x) for x in paste_accounts] ### SERIAL start_time = time.time() diff --git a/hibp/hibp.py b/hibp/hibp.py index cb9893c..65e7fff 100644 --- a/hibp/hibp.py +++ b/hibp/hibp.py @@ -45,6 +45,7 @@ class Services(Enum): Breach = "breach" AllBreaches = "allbreaches" DataClasses = "dataclasses" + PasteAccount = "pasteaccount" # generic HIBP class class HIBP(object): @@ -123,6 +124,25 @@ def get_breach(cls,name, api_version=DEFAULT_API_VERSION): req.param = name return req + @classmethod + def get_paste_account(cls, name, api_version=DEFAULT_API_VERSION): + ''' + Setup request to retrieve all pasted on HIBP for a givent website. + + Args: + - name -> name of paste account you want to query. + - api_version -> the server's requested api version: e.g v1 or v2 + + Returns: + - HIBP object with updated url, service, and param attributes + ''' + req = cls() + req.url = BASE_URL.format(api_version=api_version) + \ + "pasteaccount/{}".format(name) + req.service = Services.PasteAccount + req.param = name + return req + @classmethod def get_all_breaches(cls, api_version=DEFAULT_API_VERSION): ''' From d95ed0e370d7282f2f08f97d7a0b591ee048a1d3 Mon Sep 17 00:00:00 2001 From: Franck Awounang Date: Mon, 24 Jul 2017 06:37:31 +0200 Subject: [PATCH 4/5] updated sub-modules. --- build/lib/hibp.py | 90 ++++++++++++++++++++++++++++++++++------- build/lib/hibp/hibp.py | 92 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 152 insertions(+), 30 deletions(-) diff --git a/build/lib/hibp.py b/build/lib/hibp.py index 08f7cc9..65e7fff 100644 --- a/build/lib/hibp.py +++ b/build/lib/hibp.py @@ -1,5 +1,6 @@ from enum import Enum import re +import time try: import requests @@ -16,8 +17,26 @@ monkey.patch_all(thread=False, select=False) # global variables -BASE_URL = "https://haveibeenpwned.com/api/v2/" +BASE_URL = "https://haveibeenpwned.com/api/{api_version}/" HEADERS = {"User-Agent": "hibp-python",} +DEFAULT_API_VERSION = "v2" +#API delay between two calls +API_CALL_DELAY = 1.601 + +# decorator for api min-delay calls +def api_min_delay(base_func): + '''Forces the called function to wait until the api's call delay + is ellapsed before returning, to avoid exceeding rate.''' + def func(*args, **kwargs): + start = time.time() + res = base_func(*args, **kwargs) + stop = time.time() + ellapsed_time = stop-start + remaining_time = max(API_CALL_DELAY - ellapsed_time, 0) + time.sleep(remaining_time) + return res + return func + # enumerate the types of services that are callable class Services(Enum): @@ -26,6 +45,7 @@ class Services(Enum): Breach = "breach" AllBreaches = "allbreaches" DataClasses = "dataclasses" + PasteAccount = "pasteaccount" # generic HIBP class class HIBP(object): @@ -45,30 +65,33 @@ def __init__(self): self.response = None @classmethod - def get_account_breaches(cls,account): + def get_account_breaches(cls,account, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all breaches on a particular account Args: - account -> account you want to query. can be email or username to anything + - api_version -> the server's requested api version: e.g v1 or v2 Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "breachedaccount/{}".format(account) + req.url = BASE_URL.format(api_version=api_version) + \ + "breachedaccount/{}".format(account) req.service = Services.AccountBreach req.param = account return req @classmethod - def get_domain_breaches(cls,domain): + def get_domain_breaches(cls,domain, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all breaches on a particular domain Args: - domain -> domain you want to query. must be valid domain, according to RFC 1035 + - api_version -> the server's requested api version: e.g v1 or v2 Returns: - HIBP object with updated url, service, and param attributes ''' @@ -76,51 +99,79 @@ def get_domain_breaches(cls,domain): domain_regex = re.compile(r"[a-zA-Z\d-]{,63}(\.[a-zA-Z\d-]{,63})+") if not re.match(domain_regex, domain): raise ValueError("{} is an invalid domain.".format(domain)) - req.url = BASE_URL + "breaches?domain={}".format(domain) + req.url = BASE_URL.format(api_version=api_version) + \ + "breaches?domain={}".format(domain) req.service = Services.DomainBreach req.param = domain return req @classmethod - def get_breach(cls,name): + def get_breach(cls,name, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve a specific breach. Args: - name -> name of breach you want to query. To get a list of all breach names, run HIBP.get_all_breaches() + - api_version -> the server's requested api version: e.g v1 or v2 Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "breach/{}".format(name) + req.url = BASE_URL.format(api_version=api_version) + \ + "breach/{}".format(name) req.service = Services.Breach req.param = name return req @classmethod - def get_all_breaches(cls): + def get_paste_account(cls, name, api_version=DEFAULT_API_VERSION): + ''' + Setup request to retrieve all pasted on HIBP for a givent website. + + Args: + - name -> name of paste account you want to query. + - api_version -> the server's requested api version: e.g v1 or v2 + + Returns: + - HIBP object with updated url, service, and param attributes + ''' + req = cls() + req.url = BASE_URL.format(api_version=api_version) + \ + "pasteaccount/{}".format(name) + req.service = Services.PasteAccount + req.param = name + return req + + @classmethod + def get_all_breaches(cls, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all breaches recorded on HIBP.com so far. + Args: + - api_version -> the server's requested api version: e.g v1 or v2 + Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "breaches" + req.url = BASE_URL.format(api_version=api_version) + "breaches" req.service = Services.AllBreaches return req @classmethod - def get_dataclasses(cls): + def get_dataclasses(cls, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all dataclasses on HIBP. + Args: + - api_version -> the server's requested api version: e.g v1 or v2 + Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "dataclasses" + req.url = BASE_URL.format(api_version=api_version) + "dataclasses" req.service = Services.DataClasses return req @@ -128,6 +179,8 @@ def execute(self): ''' Execute a GET request on HIBP REST API service based on request object setup with one of the query services above. + If many queries are to be executed in batch, use @execute_min_delay + instead. Returns: - If query parameter is pwned: @@ -153,10 +206,19 @@ def execute(self): return self elif response.status_code == 404 and self.service == Services.Breach: raise ValueError("invalid breach name {}.".format(self.param)) + elif response.status_code == 429 and self.service == Services.AccountBreach: + raise ValueError("Rate limit error {}.".format(self.param)) else: self.response = response.json() return self + def execute_min_delay(self): + '''Calls execute and make sure the minimal delay between two api calls + is ellapsed before returning.''' + delayed_func = api_min_delay(self.execute) + return delayed_func() + + class AsyncHIBP(object): ''' Generic AsyncHIBP object. Use this object to do concurrent HIBP requests @@ -178,6 +240,7 @@ def __init__(self): self.url = None self.response = None + @api_min_delay def send(self,hibp_obj): ''' Spawns gevent/pool threads that will run the execute method on each @@ -208,6 +271,5 @@ def imap(self,hibp_objs): Attributes: - hibp_objs - list of HIBP objects ''' - for hibp_obj in self.pool.imap_unordered(HIBP.execute, hibp_objs): - yield hibp_obj.response - self.pool.join() + for hibp_obj in hibp_objs: + yield HIBP.execute_min_delay(hibp_obj) diff --git a/build/lib/hibp/hibp.py b/build/lib/hibp/hibp.py index 64b84a0..65e7fff 100644 --- a/build/lib/hibp/hibp.py +++ b/build/lib/hibp/hibp.py @@ -1,7 +1,6 @@ from enum import Enum import re - - +import time try: import requests @@ -18,8 +17,26 @@ monkey.patch_all(thread=False, select=False) # global variables -BASE_URL = "https://haveibeenpwned.com/api/v2/" +BASE_URL = "https://haveibeenpwned.com/api/{api_version}/" HEADERS = {"User-Agent": "hibp-python",} +DEFAULT_API_VERSION = "v2" +#API delay between two calls +API_CALL_DELAY = 1.601 + +# decorator for api min-delay calls +def api_min_delay(base_func): + '''Forces the called function to wait until the api's call delay + is ellapsed before returning, to avoid exceeding rate.''' + def func(*args, **kwargs): + start = time.time() + res = base_func(*args, **kwargs) + stop = time.time() + ellapsed_time = stop-start + remaining_time = max(API_CALL_DELAY - ellapsed_time, 0) + time.sleep(remaining_time) + return res + return func + # enumerate the types of services that are callable class Services(Enum): @@ -28,6 +45,7 @@ class Services(Enum): Breach = "breach" AllBreaches = "allbreaches" DataClasses = "dataclasses" + PasteAccount = "pasteaccount" # generic HIBP class class HIBP(object): @@ -47,30 +65,33 @@ def __init__(self): self.response = None @classmethod - def get_account_breaches(cls,account): + def get_account_breaches(cls,account, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all breaches on a particular account Args: - account -> account you want to query. can be email or username to anything + - api_version -> the server's requested api version: e.g v1 or v2 Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "breachedaccount/{}".format(account) + req.url = BASE_URL.format(api_version=api_version) + \ + "breachedaccount/{}".format(account) req.service = Services.AccountBreach req.param = account return req @classmethod - def get_domain_breaches(cls,domain): + def get_domain_breaches(cls,domain, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all breaches on a particular domain Args: - domain -> domain you want to query. must be valid domain, according to RFC 1035 + - api_version -> the server's requested api version: e.g v1 or v2 Returns: - HIBP object with updated url, service, and param attributes ''' @@ -78,51 +99,79 @@ def get_domain_breaches(cls,domain): domain_regex = re.compile(r"[a-zA-Z\d-]{,63}(\.[a-zA-Z\d-]{,63})+") if not re.match(domain_regex, domain): raise ValueError("{} is an invalid domain.".format(domain)) - req.url = BASE_URL + "breaches?domain={}".format(domain) + req.url = BASE_URL.format(api_version=api_version) + \ + "breaches?domain={}".format(domain) req.service = Services.DomainBreach req.param = domain return req @classmethod - def get_breach(cls,name): + def get_breach(cls,name, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve a specific breach. Args: - name -> name of breach you want to query. To get a list of all breach names, run HIBP.get_all_breaches() + - api_version -> the server's requested api version: e.g v1 or v2 Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "breach/{}".format(name) + req.url = BASE_URL.format(api_version=api_version) + \ + "breach/{}".format(name) req.service = Services.Breach req.param = name return req @classmethod - def get_all_breaches(cls): + def get_paste_account(cls, name, api_version=DEFAULT_API_VERSION): + ''' + Setup request to retrieve all pasted on HIBP for a givent website. + + Args: + - name -> name of paste account you want to query. + - api_version -> the server's requested api version: e.g v1 or v2 + + Returns: + - HIBP object with updated url, service, and param attributes + ''' + req = cls() + req.url = BASE_URL.format(api_version=api_version) + \ + "pasteaccount/{}".format(name) + req.service = Services.PasteAccount + req.param = name + return req + + @classmethod + def get_all_breaches(cls, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all breaches recorded on HIBP.com so far. + Args: + - api_version -> the server's requested api version: e.g v1 or v2 + Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "breaches" + req.url = BASE_URL.format(api_version=api_version) + "breaches" req.service = Services.AllBreaches return req @classmethod - def get_dataclasses(cls): + def get_dataclasses(cls, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all dataclasses on HIBP. + Args: + - api_version -> the server's requested api version: e.g v1 or v2 + Returns: - HIBP object with updated url, service, and param attributes ''' req = cls() - req.url = BASE_URL + "dataclasses" + req.url = BASE_URL.format(api_version=api_version) + "dataclasses" req.service = Services.DataClasses return req @@ -130,6 +179,8 @@ def execute(self): ''' Execute a GET request on HIBP REST API service based on request object setup with one of the query services above. + If many queries are to be executed in batch, use @execute_min_delay + instead. Returns: - If query parameter is pwned: @@ -155,9 +206,18 @@ def execute(self): return self elif response.status_code == 404 and self.service == Services.Breach: raise ValueError("invalid breach name {}.".format(self.param)) + elif response.status_code == 429 and self.service == Services.AccountBreach: + raise ValueError("Rate limit error {}.".format(self.param)) else: self.response = response.json() return self + + def execute_min_delay(self): + '''Calls execute and make sure the minimal delay between two api calls + is ellapsed before returning.''' + delayed_func = api_min_delay(self.execute) + return delayed_func() + class AsyncHIBP(object): ''' @@ -180,6 +240,7 @@ def __init__(self): self.url = None self.response = None + @api_min_delay def send(self,hibp_obj): ''' Spawns gevent/pool threads that will run the execute method on each @@ -210,6 +271,5 @@ def imap(self,hibp_objs): Attributes: - hibp_objs - list of HIBP objects ''' - for hibp_obj in self.pool.imap_unordered(HIBP.execute, hibp_objs): - yield hibp_obj.response - self.pool.join() + for hibp_obj in hibp_objs: + yield HIBP.execute_min_delay(hibp_obj) From b63fc55c4eba1f912a46d0ca344cdb880f49ff60 Mon Sep 17 00:00:00 2001 From: Franck Awounang Date: Mon, 24 Jul 2017 15:45:33 +0200 Subject: [PATCH 5/5] Response is set to 'object has not been pwned.' on 404 status for paste-acounts. Updated submodules. --- build/lib/hibp.py | 15 ++++++++++----- build/lib/hibp/example.py | 6 ++++-- build/lib/hibp/hibp.py | 15 ++++++++++----- hibp/hibp.py | 15 ++++++++++----- 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/build/lib/hibp.py b/build/lib/hibp.py index 65e7fff..c6cdb17 100644 --- a/build/lib/hibp.py +++ b/build/lib/hibp.py @@ -20,7 +20,8 @@ BASE_URL = "https://haveibeenpwned.com/api/{api_version}/" HEADERS = {"User-Agent": "hibp-python",} DEFAULT_API_VERSION = "v2" -#API delay between two calls + +#Min delay between two api calls API_CALL_DELAY = 1.601 # decorator for api min-delay calls @@ -125,12 +126,13 @@ def get_breach(cls,name, api_version=DEFAULT_API_VERSION): return req @classmethod - def get_paste_account(cls, name, api_version=DEFAULT_API_VERSION): + def get_paste_account(cls, account, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all pasted on HIBP for a givent website. Args: - - name -> name of paste account you want to query. + - account -> account you want to query. can be email or username to + anything - api_version -> the server's requested api version: e.g v1 or v2 Returns: @@ -138,9 +140,9 @@ def get_paste_account(cls, name, api_version=DEFAULT_API_VERSION): ''' req = cls() req.url = BASE_URL.format(api_version=api_version) + \ - "pasteaccount/{}".format(name) + "pasteaccount/{}".format(account) req.service = Services.PasteAccount - req.param = name + req.param = account return req @classmethod @@ -204,6 +206,9 @@ def execute(self): elif response.text == "[]" and self.service == Services.DomainBreach: self.response = "object has not been pwned." return self + elif response.status_code == 404 and self.service == Services.PasteAccount: + self.response = "object has not been pwned." + return self elif response.status_code == 404 and self.service == Services.Breach: raise ValueError("invalid breach name {}.".format(self.param)) elif response.status_code == 429 and self.service == Services.AccountBreach: diff --git a/build/lib/hibp/example.py b/build/lib/hibp/example.py index 5cc7433..6dba2fe 100644 --- a/build/lib/hibp/example.py +++ b/build/lib/hibp/example.py @@ -10,16 +10,18 @@ names = ['adobe','ashleymadison', 'naughtyamerica', 'myspace'] accounts = ["ssgrn", "pegasos1","barackobama"] domains = ['twitter.com', 'facebook.com','github.com','adobe.com'] + paste_accounts = ['test@example.com'] # setup HIBP objects for request executions reqs = [HIBP.get_breach(x) for x in names] \ + [HIBP.get_account_breaches(x) for x in accounts] \ - + [HIBP.get_domain_breaches(x) for x in domains] + + [HIBP.get_domain_breaches(x) for x in domains] \ + + [HIBP.get_paste_account(x) for x in paste_accounts] ### SERIAL start_time = time.time() for req in reqs: - req.execute() + req.execute_min_delay() elapsed_time = time.time() - start_time logging.info("serial impl took %.2f seconds" % elapsed_time) diff --git a/build/lib/hibp/hibp.py b/build/lib/hibp/hibp.py index 65e7fff..c6cdb17 100644 --- a/build/lib/hibp/hibp.py +++ b/build/lib/hibp/hibp.py @@ -20,7 +20,8 @@ BASE_URL = "https://haveibeenpwned.com/api/{api_version}/" HEADERS = {"User-Agent": "hibp-python",} DEFAULT_API_VERSION = "v2" -#API delay between two calls + +#Min delay between two api calls API_CALL_DELAY = 1.601 # decorator for api min-delay calls @@ -125,12 +126,13 @@ def get_breach(cls,name, api_version=DEFAULT_API_VERSION): return req @classmethod - def get_paste_account(cls, name, api_version=DEFAULT_API_VERSION): + def get_paste_account(cls, account, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all pasted on HIBP for a givent website. Args: - - name -> name of paste account you want to query. + - account -> account you want to query. can be email or username to + anything - api_version -> the server's requested api version: e.g v1 or v2 Returns: @@ -138,9 +140,9 @@ def get_paste_account(cls, name, api_version=DEFAULT_API_VERSION): ''' req = cls() req.url = BASE_URL.format(api_version=api_version) + \ - "pasteaccount/{}".format(name) + "pasteaccount/{}".format(account) req.service = Services.PasteAccount - req.param = name + req.param = account return req @classmethod @@ -204,6 +206,9 @@ def execute(self): elif response.text == "[]" and self.service == Services.DomainBreach: self.response = "object has not been pwned." return self + elif response.status_code == 404 and self.service == Services.PasteAccount: + self.response = "object has not been pwned." + return self elif response.status_code == 404 and self.service == Services.Breach: raise ValueError("invalid breach name {}.".format(self.param)) elif response.status_code == 429 and self.service == Services.AccountBreach: diff --git a/hibp/hibp.py b/hibp/hibp.py index 65e7fff..c6cdb17 100644 --- a/hibp/hibp.py +++ b/hibp/hibp.py @@ -20,7 +20,8 @@ BASE_URL = "https://haveibeenpwned.com/api/{api_version}/" HEADERS = {"User-Agent": "hibp-python",} DEFAULT_API_VERSION = "v2" -#API delay between two calls + +#Min delay between two api calls API_CALL_DELAY = 1.601 # decorator for api min-delay calls @@ -125,12 +126,13 @@ def get_breach(cls,name, api_version=DEFAULT_API_VERSION): return req @classmethod - def get_paste_account(cls, name, api_version=DEFAULT_API_VERSION): + def get_paste_account(cls, account, api_version=DEFAULT_API_VERSION): ''' Setup request to retrieve all pasted on HIBP for a givent website. Args: - - name -> name of paste account you want to query. + - account -> account you want to query. can be email or username to + anything - api_version -> the server's requested api version: e.g v1 or v2 Returns: @@ -138,9 +140,9 @@ def get_paste_account(cls, name, api_version=DEFAULT_API_VERSION): ''' req = cls() req.url = BASE_URL.format(api_version=api_version) + \ - "pasteaccount/{}".format(name) + "pasteaccount/{}".format(account) req.service = Services.PasteAccount - req.param = name + req.param = account return req @classmethod @@ -204,6 +206,9 @@ def execute(self): elif response.text == "[]" and self.service == Services.DomainBreach: self.response = "object has not been pwned." return self + elif response.status_code == 404 and self.service == Services.PasteAccount: + self.response = "object has not been pwned." + return self elif response.status_code == 404 and self.service == Services.Breach: raise ValueError("invalid breach name {}.".format(self.param)) elif response.status_code == 429 and self.service == Services.AccountBreach: