code.gitea.io/gitea@v1.19.3/modules/auth/password/pwn/pwn_test.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package pwn
     5  
     6  import (
     7  	"errors"
     8  	"math/rand"
     9  	"net/http"
    10  	"os"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  var client = New(WithHTTP(&http.Client{
    17  	Timeout: time.Second * 2,
    18  }))
    19  
    20  func TestMain(m *testing.M) {
    21  	rand.Seed(time.Now().Unix())
    22  	os.Exit(m.Run())
    23  }
    24  
    25  func TestPassword(t *testing.T) {
    26  	// Check input error
    27  	_, err := client.CheckPassword("", false)
    28  	if err == nil {
    29  		t.Log("blank input should return an error")
    30  		t.Fail()
    31  	}
    32  	if !errors.Is(err, ErrEmptyPassword) {
    33  		t.Log("blank input should return ErrEmptyPassword")
    34  		t.Fail()
    35  	}
    36  
    37  	// Should fail
    38  	fail := "password1234"
    39  	count, err := client.CheckPassword(fail, false)
    40  	if err != nil {
    41  		t.Log(err)
    42  		t.Fail()
    43  	}
    44  	if count == 0 {
    45  		t.Logf("%s should fail as a password\n", fail)
    46  		t.Fail()
    47  	}
    48  
    49  	// Should fail (with padding)
    50  	failPad := "administrator"
    51  	count, err = client.CheckPassword(failPad, true)
    52  	if err != nil {
    53  		t.Log(err)
    54  		t.Fail()
    55  	}
    56  	if count == 0 {
    57  		t.Logf("%s should fail as a password\n", failPad)
    58  		t.Fail()
    59  	}
    60  
    61  	// Checking for a "good" password isn't going to be perfect, but we can give it a good try
    62  	// with hopefully minimal error. Try five times?
    63  	var good bool
    64  	var pw string
    65  	for idx := 0; idx <= 5; idx++ {
    66  		pw = testPassword()
    67  		count, err = client.CheckPassword(pw, false)
    68  		if err != nil {
    69  			t.Log(err)
    70  			t.Fail()
    71  		}
    72  		if count == 0 {
    73  			good = true
    74  			break
    75  		}
    76  	}
    77  	if !good {
    78  		t.Log("no generated passwords passed. there is a chance this is a fluke")
    79  		t.Fail()
    80  	}
    81  
    82  	// Again, but with padded responses
    83  	good = false
    84  	for idx := 0; idx <= 5; idx++ {
    85  		pw = testPassword()
    86  		count, err = client.CheckPassword(pw, true)
    87  		if err != nil {
    88  			t.Log(err)
    89  			t.Fail()
    90  		}
    91  		if count == 0 {
    92  			good = true
    93  			break
    94  		}
    95  	}
    96  	if !good {
    97  		t.Log("no generated passwords passed. there is a chance this is a fluke")
    98  		t.Fail()
    99  	}
   100  }
   101  
   102  // Credit to https://golangbyexample.com/generate-random-password-golang/
   103  // DO NOT USE THIS FOR AN ACTUAL PASSWORD GENERATOR
   104  var (
   105  	lowerCharSet   = "abcdedfghijklmnopqrst"
   106  	upperCharSet   = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
   107  	specialCharSet = "!@#$%&*"
   108  	numberSet      = "0123456789"
   109  	allCharSet     = lowerCharSet + upperCharSet + specialCharSet + numberSet
   110  )
   111  
   112  func testPassword() string {
   113  	var password strings.Builder
   114  
   115  	// Set special character
   116  	for i := 0; i < 5; i++ {
   117  		random := rand.Intn(len(specialCharSet))
   118  		password.WriteString(string(specialCharSet[random]))
   119  	}
   120  
   121  	// Set numeric
   122  	for i := 0; i < 5; i++ {
   123  		random := rand.Intn(len(numberSet))
   124  		password.WriteString(string(numberSet[random]))
   125  	}
   126  
   127  	// Set uppercase
   128  	for i := 0; i < 5; i++ {
   129  		random := rand.Intn(len(upperCharSet))
   130  		password.WriteString(string(upperCharSet[random]))
   131  	}
   132  
   133  	for i := 0; i < 5; i++ {
   134  		random := rand.Intn(len(allCharSet))
   135  		password.WriteString(string(allCharSet[random]))
   136  	}
   137  	inRune := []rune(password.String())
   138  	rand.Shuffle(len(inRune), func(i, j int) {
   139  		inRune[i], inRune[j] = inRune[j], inRune[i]
   140  	})
   141  	return string(inRune)
   142  }