Skip to content

Commit

Permalink
Updated to the modern era
Browse files Browse the repository at this point in the history
  • Loading branch information
plasticuproject committed Sep 1, 2024
1 parent 6ef1310 commit 391625c
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 103 deletions.
103 changes: 81 additions & 22 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,87 @@
name: Upload Python Package

on:
push:
branches: [ master ]

# name: Publish Python 🐍 distribution 📦 to PyPI
name: Build Python distribution and Release

on: push

jobs:
deploy:

build:
name: Build distribution 📦
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.11"
- name: Install pypa/build
run: >-
python3 -m
pip install
build
--user
- name: Build a binary wheel and a source tarball
run: python3 -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/

publish-to-pypi:
name: >-
Publish Python 🐍 distribution 📦 to PyPI
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
needs:
- build
runs-on: ubuntu-latest
environment:
name: Pypi
url: https://pypi.org/p/hibpwned
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing

steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

github-release:
name: >-
Sign the Python 🐍 distribution 📦 with Sigstore
and upload them to GitHub Release
if: startsWith(github.ref, 'refs/tags/')
needs:
- publish-to-pypi
runs-on: ubuntu-latest

permissions:
contents: write # IMPORTANT: mandatory for making GitHub Releases
id-token: write # IMPORTANT: mandatory for sigstore

steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
python-version: '3.8'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python setup.py sdist
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
name: python-package-distributions
path: dist/
- name: Sign the dists with Sigstore
uses: sigstore/[email protected]
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
inputs: >-
./dist/*.tar.gz
./dist/*.whl
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release create
'${{ github.ref_name }}' dist/**
--repo '${{ github.repository }}'
10 changes: 5 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
- uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.8"
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install requests
pip install flake8 coverage>=6.3 mypy types-requests
- name: Lint with flake8
run: |
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[![build](https://github.com/plasticuproject/hibpwned/actions/workflows/tests.yml/badge.svg)](https://github.com/plasticuproject/hibpwned/actions/workflows/tests.yml)
[![Python 3.8](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/release/python-380/)
[![Python 3.11](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/release/python-311/)
[![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
[![PyPI version](https://badge.fury.io/py/hibpwned.svg)](https://badge.fury.io/py/hibpwned)
[![Downloads](https://pepy.tech/badge/hibpwned)](https://pepy.tech/project/hibpwned)
Expand Down
66 changes: 39 additions & 27 deletions hibpwned/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
"""

from __future__ import annotations
import hashlib
from typing import Dict, List, Union, Optional
import requests


Expand Down Expand Up @@ -197,21 +196,23 @@ class Pwned:
>>> foo = Pwned("[email protected]", "My_App", "My_API_Key")
>>> data = foo.search_password("BadPassword")
"""
ReturnAlias = (Union[int, List[Dict[str, Union[str, int, bool]]]])
AltReturnAlias = (Union[int, List[Dict[str, Union[str, int, bool]]],
List[str]])
DataAlias = (Union[List[Dict[str, Union[str, int, bool]]],
Dict[str, Union[str, int, bool]]])
AltDataAlias = (Union[List[Dict[str, Union[str, int, bool]]],
Dict[str, Union[str, int, bool]], List[str]])
ReturnAlias = (int | list[dict[str, str | int | bool]])

AltReturnAlias = (int | list[dict[str, str | int | bool]] | list[str])

DataAlias = (list[dict[str, str | int | bool]]
| dict[str, str | int | bool])
AltDataAlias = (list[dict[str, str | int | bool]]
| dict[str, str | int | bool] | list[str])

url: str
resp: requests.models.Response
truncate_string: str
domain_string: str
unverified_string: str
classes: List[str]
classes: list[str]
hashes: str
hash_list: List[str]
hash_list: list[str]
hexdig: str
hsh: str
pnum: str
Expand All @@ -222,17 +223,17 @@ def __init__(self, account: str, agent: str, key: str) -> None:
self.account = account
self.agent = agent
self.key = key
self.header: Dict[str, str] = {
self.header: dict[str, str] = {
"User-Agent": self.agent,
"hibp-api-key": self.key
}
self.timeout = 300

# pylint: disable=undefined-variable
def search_all_breaches(
self,
truncate: Optional[bool] = False,
domain: Optional[str] = None,
unverified: Optional[bool] = False) -> AltReturnAlias:
def search_all_breaches(self,
truncate: bool | None = False,
domain: str | None = None,
unverified: bool | None = False) -> AltReturnAlias:
"""The most common use of the API is to return a list of all
breaches a particular account has been involved in.
Expand Down Expand Up @@ -285,6 +286,7 @@ def search_all_breaches(
unverified_string = ""
resp = requests.get(url + self.account + truncate_string +
domain_string + unverified_string,
timeout=self.timeout,
headers=self.header)
_check(resp)
if resp.status_code == 200:
Expand All @@ -295,7 +297,7 @@ def search_all_breaches(
return resp.status_code

# pylint: disable=undefined-variable
def all_breaches(self, domain: Optional[str] = None) -> ReturnAlias:
def all_breaches(self, domain: str | None = None) -> ReturnAlias:
"""Retrieves all breached sites from the system. The result set
can also be filtered by domain by passing the argument
"domain='example.com'". This filters the result set to only
Expand All @@ -315,7 +317,9 @@ def all_breaches(self, domain: Optional[str] = None) -> ReturnAlias:
domain_string = ""
else:
domain_string = "?domain=" + domain
resp = requests.get(url + domain_string, headers=self.header)
resp = requests.get(url + domain_string,
headers=self.header,
timeout=self.timeout)
_check(resp)
if resp.status_code == 200:
data = resp.json()
Expand All @@ -337,7 +341,9 @@ def single_breach(self, name: str) -> ReturnAlias:
>>> data = foo.single_breach("adobe")
"""
url = "https://haveibeenpwned.com/api/v3/breach/"
resp = requests.get(url + name, headers=self.header)
resp = requests.get(url + name,
headers=self.header,
timeout=self.timeout)
_check(resp)
if resp.status_code == 200:
data = resp.json()
Expand All @@ -346,7 +352,7 @@ def single_breach(self, name: str) -> ReturnAlias:
# return data # Pretty sure will never hit
return resp.status_code

def data_classes(self) -> Union[int, List[str]]:
def data_classes(self) -> int | list[str]:
"""Returns all data classes in the system.
Expand All @@ -356,7 +362,7 @@ def data_classes(self) -> Union[int, List[str]]:
>>> data = foo.data_classes()
"""
url = "https://haveibeenpwned.com/api/v3/dataclasses"
resp = requests.get(url, headers=self.header)
resp = requests.get(url, headers=self.header, timeout=self.timeout)
_check(resp)
if resp.status_code == 200:
classes = resp.json()
Expand Down Expand Up @@ -411,7 +417,9 @@ def search_pastes(self) -> ReturnAlias:
>>> data = foo.search_pastes()
"""
url = "https://haveibeenpwned.com/api/v3/pasteaccount/"
resp = requests.get(url + self.account, headers=self.header)
resp = requests.get(url + self.account,
headers=self.header,
timeout=self.timeout)
_check(resp)
if resp.status_code == 200:
data = resp.json()
Expand All @@ -420,7 +428,7 @@ def search_pastes(self) -> ReturnAlias:
return data
return resp.status_code

def search_password(self, password: str) -> Union[int, str]:
def search_password(self, password: str) -> int | str:
"""Returns an integer of how many times the password appears in
the Pwned Passwords repository, where each password is stored
as a SHA-1 hash of a UTF-8 encoded password. When a password
Expand Down Expand Up @@ -450,7 +458,9 @@ def search_password(self, password: str) -> Union[int, str]:
hexdig = hexdig.upper()
hsh = hexdig[:5]
pnum = '0'
resp = requests.get(url + hsh, headers=self.header)
resp = requests.get(url + hsh,
headers=self.header,
timeout=self.timeout)
_check(resp)
if resp.status_code == 200:
hash_list = resp.text.splitlines()
Expand All @@ -460,7 +470,7 @@ def search_password(self, password: str) -> Union[int, str]:
return pnum
return resp.status_code

def search_hashes(self, hsh: str) -> Union[int, str]:
def search_hashes(self, hsh: str) -> int | str:
"""Returns a string of plaintext hashes which are suffixes to the
first 5 characters of the searched hash argument. When a
password hash with the same first 5 characters is found in the
Expand Down Expand Up @@ -488,7 +498,9 @@ def search_hashes(self, hsh: str) -> Union[int, str]:
"""
url = "https://api.pwnedpasswords.com/range/"
hsh = hsh[:5]
resp = requests.get(url + hsh, headers=self.header)
resp = requests.get(url + hsh,
headers=self.header,
timeout=self.timeout)
_check(resp)
if resp.status_code == 200:
hashes = resp.text
Expand Down
27 changes: 23 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "hibpwned"
version = "1.3.8"
authors = [
{ name="plasticuproject", email="[email protected]" },
]
description = "A human friendly Python API wrapper for haveibeenpwned.com"
readme = "README.md"
keywords = ["hibp", "haveibeenpwned", "api", "wrapper"]
requires-python = ">=3.11"
classifiers = [
"Programming Language :: Python :: 3",
"Development Status :: 5 - Production/Stable", "Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: OS Independent",
"Topic :: Utilities", "Topic :: Utilities"
]
dependencies = ["requests>=2.32.3"]

[project.urls]
"Homepage" = "https://github.com/plasticuproject/hibpwned"
1 change: 0 additions & 1 deletion requirements.txt

This file was deleted.

31 changes: 0 additions & 31 deletions setup.py

This file was deleted.

Loading

0 comments on commit 391625c

Please sign in to comment.