mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-27 20:41:01 +00:00 
			
		
		
		
	- Massive replacement of changing `code.gitea.io/gitea` to `forgejo.org`. - Resolves forgejo/discussions#258 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7337 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Reviewed-by: Beowulf <beowulf@beocode.eu> Reviewed-by: Panagiotis "Ivory" Vasilopoulos <git@n0toose.net> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-committed-by: Gusted <postmaster@gusted.xyz>
		
			
				
	
	
		
			118 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			118 lines
		
	
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2023 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package pwn
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"crypto/sha1"
 | |
| 	"encoding/hex"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"forgejo.org/modules/setting"
 | |
| )
 | |
| 
 | |
| const passwordURL = "https://api.pwnedpasswords.com/range/"
 | |
| 
 | |
| // ErrEmptyPassword is an empty password error
 | |
| var ErrEmptyPassword = errors.New("password cannot be empty")
 | |
| 
 | |
| // Client is a HaveIBeenPwned client
 | |
| type Client struct {
 | |
| 	ctx  context.Context
 | |
| 	http *http.Client
 | |
| }
 | |
| 
 | |
| // New returns a new HaveIBeenPwned Client
 | |
| func New(options ...ClientOption) *Client {
 | |
| 	client := &Client{
 | |
| 		ctx:  context.Background(),
 | |
| 		http: http.DefaultClient,
 | |
| 	}
 | |
| 
 | |
| 	for _, opt := range options {
 | |
| 		opt(client)
 | |
| 	}
 | |
| 
 | |
| 	return client
 | |
| }
 | |
| 
 | |
| // ClientOption is a way to modify a new Client
 | |
| type ClientOption func(*Client)
 | |
| 
 | |
| // WithHTTP will set the http.Client of a Client
 | |
| func WithHTTP(httpClient *http.Client) func(pwnClient *Client) {
 | |
| 	return func(pwnClient *Client) {
 | |
| 		pwnClient.http = httpClient
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WithContext will set the context.Context of a Client
 | |
| func WithContext(ctx context.Context) func(pwnClient *Client) {
 | |
| 	return func(pwnClient *Client) {
 | |
| 		pwnClient.ctx = ctx
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newRequest(ctx context.Context, method, url string, body io.ReadCloser) (*http.Request, error) {
 | |
| 	req, err := http.NewRequestWithContext(ctx, method, url, body)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	req.Header.Add("User-Agent", "Gitea "+setting.AppVer)
 | |
| 	return req, nil
 | |
| }
 | |
| 
 | |
| // CheckPassword returns the number of times a password has been compromised
 | |
| // Adding padding will make requests more secure, however is also slower
 | |
| // because artificial responses will be added to the response
 | |
| // For more information, see https://www.troyhunt.com/enhancing-pwned-passwords-privacy-with-padding/
 | |
| func (c *Client) CheckPassword(pw string, padding bool) (int, error) {
 | |
| 	if pw == "" {
 | |
| 		return -1, ErrEmptyPassword
 | |
| 	}
 | |
| 
 | |
| 	sha := sha1.New()
 | |
| 	sha.Write([]byte(pw))
 | |
| 	enc := hex.EncodeToString(sha.Sum(nil))
 | |
| 	prefix, suffix := enc[:5], enc[5:]
 | |
| 
 | |
| 	req, err := newRequest(c.ctx, http.MethodGet, fmt.Sprintf("%s%s", passwordURL, prefix), nil)
 | |
| 	if err != nil {
 | |
| 		return -1, nil
 | |
| 	}
 | |
| 	if padding {
 | |
| 		req.Header.Add("Add-Padding", "true")
 | |
| 	}
 | |
| 
 | |
| 	resp, err := c.http.Do(req)
 | |
| 	if err != nil {
 | |
| 		return -1, err
 | |
| 	}
 | |
| 
 | |
| 	body, err := io.ReadAll(resp.Body)
 | |
| 	if err != nil {
 | |
| 		return -1, err
 | |
| 	}
 | |
| 	defer resp.Body.Close()
 | |
| 
 | |
| 	for _, pair := range strings.Split(string(body), "\n") {
 | |
| 		parts := strings.Split(pair, ":")
 | |
| 		if len(parts) != 2 {
 | |
| 			continue
 | |
| 		}
 | |
| 		if strings.EqualFold(suffix, parts[0]) {
 | |
| 			count, err := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64)
 | |
| 			if err != nil {
 | |
| 				return -1, err
 | |
| 			}
 | |
| 			return int(count), nil
 | |
| 		}
 | |
| 	}
 | |
| 	return 0, nil
 | |
| }
 |