surtur
f2025395b2
All checks were successful
continuous-integration/drone/push Build is passing
* change hibp schema's date field to string, as the date format would prevent direct unmarshaling. instead, marshal to string, convert later * the scheduler is in place in order not to get throttled after going over API limit * the scheduler detects when in testing mode and changes little bits of behaviour * add tests for some basic requests * run the requests scheduler as a background service during testing
131 lines
3.0 KiB
Go
131 lines
3.0 KiB
Go
// Copyright 2023 wanderer <a_mirre at utb dot cz>
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
package hibp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"git.dotya.ml/mirre-mt/pcmt/ent/schema"
|
|
"golang.org/x/exp/slog"
|
|
)
|
|
|
|
// Subscription models the HIBP subscription struct.
|
|
type Subscription struct {
|
|
// The name representing the subscription being either "Pwned 1", "Pwned 2", "Pwned 3" or "Pwned 4".
|
|
SubscriptionName string
|
|
// A human readable sentence explaining the scope of the subscription.
|
|
Description string
|
|
// The date and time the current subscription ends in ISO 8601 format.
|
|
SubscribedUntil time.Time
|
|
// The rate limit in requests per minute. This applies to the rate the breach search by email address API can be requested.
|
|
Rpm int
|
|
// The size of the largest domain the subscription can search. This is expressed in the total number of breached accounts on the domain, excluding those that appear solely in spam list.
|
|
DomainSearchMaxBreachedAccounts int
|
|
}
|
|
|
|
const (
|
|
api = "https://haveibeenpwned.com/api/v3"
|
|
appID = "pcmt (https://git.dotya.ml/mirre-mt/pcmt)"
|
|
// set default request timeout so as not to hang forever.
|
|
reqTmOut = 5 * time.Second
|
|
|
|
headerUA = "user-agent"
|
|
headerHIBP = "hibp-api-key"
|
|
)
|
|
|
|
var (
|
|
apiKey = os.Getenv("PCMT_HIBP_API_KEY")
|
|
client = &http.Client{Timeout: reqTmOut}
|
|
)
|
|
|
|
// SubscriptionStatus models https://haveibeenpwned.com/API/v3#SubscriptionStatus.
|
|
func SubscriptionStatus() (*Subscription, error) {
|
|
u := api + "/subscription"
|
|
|
|
req, err := http.NewRequest("GET", u, nil)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
setUA(req)
|
|
setAuthHeader(req)
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, _ := io.ReadAll(resp.Body)
|
|
|
|
// bodyString := string(body)
|
|
// fmt.Println("API Response as a string:\n" + bodyString)
|
|
|
|
var s Subscription
|
|
|
|
if err := json.Unmarshal(body, &s); err != nil {
|
|
return nil, err
|
|
}
|
|
// fmt.Printf("Subscription struct %+v\n", s)
|
|
|
|
return &Subscription{}, nil
|
|
}
|
|
|
|
// GetAllBreaches retrieves all breaches available in HIBP, as per
|
|
// https://haveibeenpwned.com/API/v3#AllBreaches.
|
|
func GetAllBreaches() (*[]schema.HIBPSchema, error) {
|
|
u := api + "/breaches"
|
|
|
|
req, err := http.NewRequest("GET", u, nil)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
respCh, errCh := rChans()
|
|
|
|
setUA(req)
|
|
slog.Info("scheduling all breaches")
|
|
scheduleReq(req, &respCh, &errCh)
|
|
slog.Info("scheduled all breaches")
|
|
|
|
resp := <-respCh
|
|
err = <-errCh
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
body, _ := io.ReadAll(resp.Body)
|
|
|
|
// bodyString := string(body)
|
|
// fmt.Println("API Response as a string:\n" + bodyString)
|
|
|
|
ab := make([]schema.HIBPSchema, 0)
|
|
|
|
if err = json.Unmarshal(body, &ab); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ab, nil
|
|
}
|
|
|
|
func setUA(r *http.Request) {
|
|
r.Header.Set(headerUA, appID)
|
|
}
|
|
|
|
func setAuthHeader(r *http.Request) {
|
|
r.Header.Set(headerHIBP, apiKey)
|
|
}
|
|
|
|
func rChans() (chan *http.Response, chan error) {
|
|
return make(chan *http.Response), make(chan error)
|
|
}
|