revamped CSS - using Tailwind now, update layout and added home page

This commit is contained in:
nahakubuilde
2025-07-19 12:58:22 +01:00
parent 20cfcd1829
commit 173569365b
17 changed files with 4318 additions and 8950 deletions
+31 -13
View File
@@ -3,6 +3,7 @@ package passwordgenerator
import (
"encoding/json"
"net/http"
"strings"
"headeranalyzer/security"
)
@@ -17,17 +18,18 @@ func PasswordAPIHandler(w http.ResponseWriter, r *http.Request) {
}
var requestData struct {
Type string `json:"type"`
Length int `json:"length"`
IncludeUpper bool `json:"includeUpper"`
IncludeLower bool `json:"includeLower"`
NumberCount int `json:"numberCount"`
SpecialChars string `json:"specialChars"`
NoConsecutive bool `json:"noConsecutive"`
WordCount int `json:"wordCount"`
NumberPosition string `json:"numberPosition"`
UseNumbers bool `json:"useNumbers"`
UseSpecial bool `json:"useSpecial"`
Type string `json:"type"`
Length int `json:"length"`
IncludeUpper bool `json:"includeUpper"`
IncludeLower bool `json:"includeLower"`
NumberCount int `json:"numberCount"`
SpecialChars string `json:"specialChars"`
MinSpecialChars int `json:"minSpecialChars"`
NoConsecutive bool `json:"noConsecutive"`
WordCount int `json:"wordCount"`
NumberPosition string `json:"numberPosition"`
UseNumbers bool `json:"useNumbers"`
UseSpecial bool `json:"useSpecial"`
}
if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil {
@@ -49,6 +51,12 @@ func PasswordAPIHandler(w http.ResponseWriter, r *http.Request) {
return
}
if requestData.MinSpecialChars < 0 || requestData.MinSpecialChars > 20 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Minimum special characters count must be between 0 and 20"))
return
}
if requestData.WordCount < 2 || requestData.WordCount > 10 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Word count must be between 2 and 10"))
@@ -72,8 +80,17 @@ func PasswordAPIHandler(w http.ResponseWriter, r *http.Request) {
requestData.NumberPosition = "end" // Default
}
// Sanitize special characters to prevent potential issues
requestData.SpecialChars = validator.SanitizeHTML(requestData.SpecialChars)
// Validate special characters - only allow specific safe characters
if len(requestData.SpecialChars) > 0 {
allowedSpecialChars := "!@#$%&*-_=+."
for _, char := range requestData.SpecialChars {
if !strings.ContainsRune(allowedSpecialChars, char) {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Special characters must only contain: !@#$%&*-_=+."))
return
}
}
}
// Convert to internal Config format
config := Config{
@@ -82,6 +99,7 @@ func PasswordAPIHandler(w http.ResponseWriter, r *http.Request) {
IncludeLower: requestData.IncludeLower,
NumberCount: requestData.NumberCount,
SpecialChars: requestData.SpecialChars,
MinSpecialChars: requestData.MinSpecialChars,
NoConsecutive: requestData.NoConsecutive,
UsePassphrase: requestData.Type == "passphrase",
WordCount: requestData.WordCount,
+111 -8
View File
@@ -18,6 +18,7 @@ type Config struct {
IncludeLower bool `json:"includeLower"`
NumberCount int `json:"numberCount"`
SpecialChars string `json:"specialChars"`
MinSpecialChars int `json:"minSpecialChars"`
NoConsecutive bool `json:"noConsecutive"`
UsePassphrase bool `json:"usePassphrase"`
WordCount int `json:"wordCount"`
@@ -232,8 +233,9 @@ func DefaultConfig() Config {
IncludeUpper: true,
IncludeLower: true,
NumberCount: 1,
SpecialChars: "!@#$%^&*-_=+",
NoConsecutive: false,
SpecialChars: "!@#$%&*-_=+.",
MinSpecialChars: 3,
NoConsecutive: true,
UsePassphrase: true, // Default to passphrase
WordCount: 3,
NumberPosition: "end",
@@ -279,6 +281,18 @@ func generateRandomPassword(config Config) (string, error) {
return "", fmt.Errorf("no character types selected")
}
// Validate that minimum special characters doesn't exceed password length
totalRequired := config.NumberCount + config.MinSpecialChars
if config.IncludeLower {
totalRequired++
}
if config.IncludeUpper {
totalRequired++
}
if totalRequired > config.Length {
return "", fmt.Errorf("password length too short for required character counts")
}
password := make([]byte, config.Length)
// Ensure at least one character from each required set
@@ -304,10 +318,50 @@ func generateRandomPassword(config Config) (string, error) {
}
}
// Then place at least one from each other required character set
// Place required special characters
specialCharsPlaced := 0
if config.MinSpecialChars > 0 && len(config.SpecialChars) > 0 {
attempts := 0
maxAttempts := config.Length * 10 // Prevent infinite loops
for specialCharsPlaced < config.MinSpecialChars && attempts < maxAttempts {
pos, err := rand.Int(rand.Reader, big.NewInt(int64(config.Length)))
if err != nil {
return "", err
}
posInt := int(pos.Int64())
if !usedPositions[posInt] {
char, err := rand.Int(rand.Reader, big.NewInt(int64(len(config.SpecialChars))))
if err != nil {
return "", err
}
password[posInt] = config.SpecialChars[char.Int64()]
usedPositions[posInt] = true
specialCharsPlaced++
}
attempts++
}
// If we couldn't place enough special characters due to bad luck, force placement
if specialCharsPlaced < config.MinSpecialChars {
for i := 0; i < config.Length && specialCharsPlaced < config.MinSpecialChars; i++ {
if !usedPositions[i] {
char, err := rand.Int(rand.Reader, big.NewInt(int64(len(config.SpecialChars))))
if err != nil {
return "", err
}
password[i] = config.SpecialChars[char.Int64()]
usedPositions[i] = true
specialCharsPlaced++
}
}
}
}
// Then place at least one from each other required character set (excluding numbers and special chars already handled)
for _, reqSet := range required {
if reqSet == "0123456789" {
continue // Already handled numbers
if reqSet == "0123456789" || reqSet == config.SpecialChars {
continue // Already handled numbers and special chars
}
placed := false
@@ -344,11 +398,60 @@ func generateRandomPassword(config Config) (string, error) {
}
// Handle no consecutive characters requirement
finalPassword := string(password)
if config.NoConsecutive {
return ensureNoConsecutive(string(password), charset)
}
finalPassword, err := ensureNoConsecutive(string(password), charset)
if err != nil {
return "", err
}
return string(password), nil
// After ensureNoConsecutive, we need to verify minimum requirements are still met
// and fix them if necessary
if config.MinSpecialChars > 0 && len(config.SpecialChars) > 0 {
// Count current special characters
currentSpecialCount := 0
for _, char := range finalPassword {
if strings.ContainsRune(config.SpecialChars, char) {
currentSpecialCount++
}
}
// If we don't have enough special characters, replace some non-special characters
if currentSpecialCount < config.MinSpecialChars {
passwordRunes := []rune(finalPassword)
needed := config.MinSpecialChars - currentSpecialCount
for i := 0; i < len(passwordRunes) && needed > 0; i++ {
// Find non-special characters that can be replaced
if !strings.ContainsRune(config.SpecialChars, passwordRunes[i]) {
// Make sure this replacement won't create consecutive characters
specialChar, err := rand.Int(rand.Reader, big.NewInt(int64(len(config.SpecialChars))))
if err != nil {
return "", err
}
newChar := rune(config.SpecialChars[specialChar.Int64()])
// Check if this would create consecutive characters
wouldCreateConsecutive := false
if i > 0 && passwordRunes[i-1] == newChar {
wouldCreateConsecutive = true
}
if i < len(passwordRunes)-1 && passwordRunes[i+1] == newChar {
wouldCreateConsecutive = true
}
if !wouldCreateConsecutive {
passwordRunes[i] = newChar
needed--
}
}
}
finalPassword = string(passwordRunes)
}
}
return finalPassword, nil
}
return finalPassword, nil
}
func generatePassphrase(config Config) (string, error) {
+27 -27
View File
@@ -19,18 +19,18 @@ type Handler struct {
}
type PasswordConfig struct {
Type string
Length int
IncludeUpper bool
IncludeLower bool
NumberCount int
SpecialChars string
NoConsecutive bool
WordCount int
UseNumbers bool
UseSpecial bool
NumberPosition string
SavePasswords bool
Type string
Length int
IncludeUpper bool
IncludeLower bool
NumberCount int
SpecialChars string
MinSpecialChars int
NoConsecutive bool
WordCount int
UseNumbers bool
UseSpecial bool
NumberPosition string
}
func NewHandler(embeddedFiles embed.FS) *Handler {
@@ -59,7 +59,7 @@ func NewHandler(embeddedFiles embed.FS) *Handler {
return 0
}
},
}).ParseFS(embeddedFiles, "web/base.html", "web/password.html"))
}).ParseFS(embeddedFiles, "web/base.html", "web/pwgenerator.html"))
return &Handler{
templates: tmpl,
@@ -72,24 +72,24 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Generate CSRF token
csrfToken, err := h.csrf.GenerateToken()
if err != nil {
http.Redirect(w, r, "/password?error="+url.QueryEscape("Security token generation failed"), http.StatusSeeOther)
http.Redirect(w, r, "/pwgenerator?error="+url.QueryEscape("Security token generation failed"), http.StatusSeeOther)
return
}
// Parse URL parameters to set default values
config := PasswordConfig{
Type: getStringParam(r, "type", "passphrase"),
Length: getIntParam(r, "length", 12),
IncludeUpper: getBoolParam(r, "includeUpper", true),
IncludeLower: getBoolParam(r, "includeLower", true),
NumberCount: getIntParam(r, "numberCount", 1),
SpecialChars: getStringParam(r, "specialChars", "!@#$%^&*-_=+"),
NoConsecutive: getBoolParam(r, "noConsecutive", false),
WordCount: getIntParam(r, "wordCount", 3),
UseNumbers: getBoolParam(r, "useNumbers", true),
UseSpecial: getBoolParam(r, "useSpecial", false),
NumberPosition: getStringParam(r, "numberPosition", "end"),
SavePasswords: getBoolParam(r, "savePasswords", false),
Type: getStringParam(r, "type", "passphrase"),
Length: getIntParam(r, "length", 12),
IncludeUpper: getBoolParam(r, "includeUpper", true),
IncludeLower: getBoolParam(r, "includeLower", true),
NumberCount: getIntParam(r, "numberCount", 2),
SpecialChars: getStringParam(r, "specialChars", "!@#$%&*-_=+."),
MinSpecialChars: getIntParam(r, "minSpecialChars", 3),
NoConsecutive: getBoolParam(r, "noConsecutive", true),
WordCount: getIntParam(r, "wordCount", 3),
UseNumbers: getBoolParam(r, "useNumbers", true),
UseSpecial: getBoolParam(r, "useSpecial", false),
NumberPosition: getStringParam(r, "numberPosition", "end"),
}
data := struct {
@@ -101,7 +101,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Config: config,
CSRFToken: csrfToken,
}
h.templates.ExecuteTemplate(w, "password.html", data)
h.templates.ExecuteTemplate(w, "pwgenerator.html", data)
}
// Helper functions to parse URL parameters