add password generator
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"maxwarden/users"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Secret struct {
|
||||
@@ -16,6 +17,8 @@ type Secret struct {
|
||||
Notes string
|
||||
Username string
|
||||
Password string
|
||||
Created time.Time
|
||||
Modified time.Time
|
||||
}
|
||||
|
||||
type EntryFilter struct {
|
||||
@@ -122,4 +125,54 @@ func DeleteSecret(userId int32, masterKey string, secretId string) error {
|
||||
_, userErr := users.Update(user)
|
||||
|
||||
return userErr
|
||||
}
|
||||
|
||||
func Update(userId int32, masterKey string, secret Secret) error {
|
||||
user, _ := users.FetchById(userId)
|
||||
|
||||
secrets, _ := security.DecryptDataWithKey[[]Secret](user.Data, masterKey)
|
||||
if secrets == nil {
|
||||
return errors.New("user secrets are null")
|
||||
}
|
||||
|
||||
secret.Modified = time.Now()
|
||||
|
||||
// linear search and replace
|
||||
for i, v := range *secrets {
|
||||
if v.ID == secret.ID {
|
||||
created := (*secrets)[i].Created
|
||||
secret.Created = created
|
||||
|
||||
(*secrets)[i] = secret
|
||||
}
|
||||
}
|
||||
|
||||
enc, _ := security.EncryptDataWithKey(secrets, masterKey)
|
||||
|
||||
user.Data = enc
|
||||
_, updateErr := users.Update(user)
|
||||
|
||||
return updateErr
|
||||
}
|
||||
|
||||
func Add(userId int32, masterKey string, secret Secret) error {
|
||||
user, _ := users.FetchById(userId)
|
||||
|
||||
secrets, _ := security.DecryptDataWithKey[[]Secret](user.Data, masterKey)
|
||||
if secrets == nil {
|
||||
return errors.New("user secrets are null")
|
||||
}
|
||||
|
||||
secret.ID = security.RandBase58String(32)
|
||||
secret.Modified = time.Now()
|
||||
secret.Created = time.Now()
|
||||
|
||||
*secrets = append(*secrets, secret)
|
||||
|
||||
enc, _ := security.EncryptDataWithKey(secrets, masterKey)
|
||||
|
||||
user.Data = enc
|
||||
_, updateErr := users.Update(user)
|
||||
|
||||
return updateErr
|
||||
}
|
||||
13
generator/passphrase.go
Normal file
13
generator/passphrase.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/sethvargo/go-diceware/diceware"
|
||||
)
|
||||
|
||||
func GeneratePassphrase(words int) string {
|
||||
list, _ := diceware.Generate(words)
|
||||
|
||||
return strings.Join(list, " ")
|
||||
}
|
||||
26
generator/password.go
Normal file
26
generator/password.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package generator
|
||||
|
||||
import "github.com/sethvargo/go-password/password"
|
||||
|
||||
func GeneratePassword(length int, digits int, symbols int, disableUpper bool, allowRepeats bool) string {
|
||||
if length <= 0 {
|
||||
length = 16
|
||||
}
|
||||
|
||||
if digits <= 0 {
|
||||
digits = 0
|
||||
}
|
||||
|
||||
if symbols <= 0 {
|
||||
symbols = 0
|
||||
}
|
||||
|
||||
result, _ := password.Generate(length, digits, symbols, disableUpper, allowRepeats)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func GenerateDefault() string {
|
||||
result, _ := password.Generate(24, 5, 5, false, false)
|
||||
return result
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -30,6 +30,8 @@ require (
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/sethvargo/go-diceware v0.5.0 // indirect
|
||||
github.com/sethvargo/go-password v0.3.1 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -70,6 +70,10 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/sethvargo/go-diceware v0.5.0 h1:exrQ7GpaBo00GqRVM1N8ChXSsi3oS7tjQiIehsD+yR0=
|
||||
github.com/sethvargo/go-diceware v0.5.0/go.mod h1:Lg1SyPS7yQO6BBgTN5r4f2MUDkqGfLWsOjHPY0kA8iw=
|
||||
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
|
||||
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
|
||||
@@ -2,9 +2,7 @@ package app
|
||||
|
||||
import (
|
||||
"maxwarden/entries"
|
||||
"maxwarden/security"
|
||||
. "maxwarden/ui"
|
||||
"maxwarden/users"
|
||||
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
@@ -56,6 +54,7 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
url := r.FormValue("url")
|
||||
|
||||
secret = entries.Secret{
|
||||
ID: r.PathValue("id"),
|
||||
Description: desc,
|
||||
URL: url,
|
||||
Notes: notes,
|
||||
@@ -63,42 +62,26 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Username: username,
|
||||
}
|
||||
|
||||
user, _ := users.FetchById(identity.UserID)
|
||||
|
||||
// Get current secret store
|
||||
secrets, _ := security.DecryptDataWithKey[[]entries.Secret](user.Data, identity.MasterKey)
|
||||
|
||||
if secrets == nil {
|
||||
http.Redirect(w, r, "/app", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
if editorType == EDITOR_TYPE_ADD {
|
||||
secret.ID = security.RandBase58String(32)
|
||||
*secrets = append(*secrets, secret)
|
||||
entries.Add(identity.UserID, identity.MasterKey, secret)
|
||||
} else {
|
||||
secret.ID = r.PathValue("id")
|
||||
|
||||
// linear search and replace
|
||||
for i, v := range *secrets {
|
||||
if v.ID == secret.ID {
|
||||
(*secrets)[i] = secret
|
||||
}
|
||||
}
|
||||
entries.Update(identity.UserID, identity.MasterKey, secret)
|
||||
}
|
||||
|
||||
// Serialize and encrypt modified store using master key
|
||||
enc, _ := security.EncryptDataWithKey(secrets, identity.MasterKey)
|
||||
|
||||
user.Data = enc
|
||||
|
||||
users.Update(user)
|
||||
|
||||
http.Redirect(w, r, "/app", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
AppLayout(title, *identity, session,
|
||||
Modal(
|
||||
"password_generator",
|
||||
Text("Passkey Generator"),
|
||||
HxLoad("/app/generator-hx"),
|
||||
[]Node {
|
||||
ButtonUIOutline(ModalCloser(), Text("Close")),
|
||||
},
|
||||
),
|
||||
|
||||
If(editorType == EDITOR_TYPE_EDIT,
|
||||
Group{
|
||||
Modal(
|
||||
@@ -106,7 +89,7 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Text("Warning!"),
|
||||
Text("Are you sure you want to delete this entry? This action cannot be undone."),
|
||||
[]Node{
|
||||
A(Href("/app/delete/" + secret.ID), ButtonUIDanger(Text("Delete"))),
|
||||
A(Href("/app/delete/"+secret.ID), ButtonUIDanger(Text("Delete"))),
|
||||
ButtonUIOutline(ModalCloser(), Text("Close")),
|
||||
},
|
||||
),
|
||||
@@ -119,16 +102,41 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Form(
|
||||
AutoComplete("off"),
|
||||
Method("POST"),
|
||||
|
||||
FormLabel(Text("Description")),
|
||||
FormInput(Type("text"), Name("description"), Value(secret.Description)),
|
||||
FormInput(Type("text"), Name("description"), Value(secret.Description), AutoFocus(), Required()),
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Username")),
|
||||
FormInput(Type("text"), Name("un"), Value(secret.Username)),
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Password")),
|
||||
FormInput(Type("password"), Name("pas"), Value(secret.Password)),
|
||||
Div(
|
||||
FlexLeftRight(
|
||||
FormLabel(Text("Password")),
|
||||
ModalActuator("password_generator", ButtonUI(Type("button"), Text("Generate a secure password"))),
|
||||
),
|
||||
Br(),
|
||||
|
||||
FormInput(Class("password"), Type("password"), Name("pas"), Value(secret.Password)),
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Show password?"), For("show")),
|
||||
FormCheck(Class("checkbox"), ID("show")),
|
||||
InlineScript(`
|
||||
let check = me(".checkbox", me());
|
||||
let passInput = me(".password", me());
|
||||
|
||||
check.on("click", () => {
|
||||
if (passInput.type === "password") {
|
||||
passInput.type = "text";
|
||||
} else if (passInput.type === "text") {
|
||||
passInput.type = "password";
|
||||
}
|
||||
});
|
||||
`),
|
||||
),
|
||||
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("URL")),
|
||||
@@ -144,6 +152,15 @@ func EditorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ButtonUISuccess(Text(btnLabel), Type("submit")),
|
||||
A(Href("/app"), ButtonUIOutline(Text("Close"), Type("button"))),
|
||||
),
|
||||
|
||||
If(editorType == EDITOR_TYPE_EDIT,
|
||||
Div(
|
||||
InlineStyle("$me { margin-top: $3; color: $color(neutral-400); font-size: var(--text-sm);}"),
|
||||
Text("Last modified: "), FormatDateTime(secret.Modified),
|
||||
Br(),
|
||||
Text("Created: "), FormatDateTime(secret.Created),
|
||||
),
|
||||
),
|
||||
),
|
||||
).Render(w)
|
||||
}
|
||||
|
||||
17
handlers/app/generator.go
Normal file
17
handlers/app/generator.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"maxwarden/middleware"
|
||||
. "maxwarden/ui"
|
||||
)
|
||||
|
||||
func GeneratorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
identity := middleware.GetIdentity(r)
|
||||
session := middleware.GetSession(r)
|
||||
|
||||
AppLayout("Passkey Generator", *identity, session,
|
||||
HxLoad("/app/generator-hx"),
|
||||
).Render(w)
|
||||
}
|
||||
114
handlers/app/generator_hx.go
Normal file
114
handlers/app/generator_hx.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"maxwarden/generator"
|
||||
. "maxwarden/ui"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
. "maxwarden/basic"
|
||||
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
hx "maragu.dev/gomponents-htmx"
|
||||
)
|
||||
|
||||
func GeneratorHxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
r.ParseForm()
|
||||
|
||||
wordCount, _ := strconv.Atoi(r.FormValue("wordCount"))
|
||||
|
||||
if wordCount <= 0 {
|
||||
wordCount = 6
|
||||
}
|
||||
|
||||
phraseOutput := generator.GeneratePassphrase(wordCount)
|
||||
|
||||
length, _ := strconv.Atoi(r.FormValue("length"))
|
||||
if length <= 0 {
|
||||
length = 24
|
||||
}
|
||||
|
||||
numbers, _ := strconv.Atoi(r.FormValue("numbers"))
|
||||
if numbers <= 0 {
|
||||
numbers = 5
|
||||
}
|
||||
|
||||
symbols, _ := strconv.Atoi(r.FormValue("symbols"))
|
||||
if symbols <= 0 {
|
||||
symbols = 5
|
||||
}
|
||||
|
||||
passwordOutput := generator.GeneratePassword(length, numbers, symbols, false, true)
|
||||
|
||||
customFormInput := func(children ...Node) Node {
|
||||
return Div(
|
||||
InlineStyle(`
|
||||
$me {
|
||||
border: 1px solid $color(neutral-300);
|
||||
margin-bottom: $3;
|
||||
}
|
||||
`),
|
||||
FormInput(
|
||||
Group(children),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Div(ID("generator"),
|
||||
Card(
|
||||
Heading("Generate Passphrase"),
|
||||
Form(
|
||||
hx.Post(r.URL.Path),
|
||||
hx.Swap("outerHTML"),
|
||||
hx.Target("#generator"),
|
||||
FormLabel(Text("Word Count")),
|
||||
customFormInput(Type("number"), Name("wordCount"), Value(ToString(wordCount))),
|
||||
|
||||
ButtonUI(Text("Generate"), Type("submit")),
|
||||
),
|
||||
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Output")),
|
||||
customFormInput(
|
||||
Value(phraseOutput),
|
||||
),
|
||||
),
|
||||
|
||||
Br(),
|
||||
|
||||
Card(
|
||||
Heading("Generate Password"),
|
||||
Form(
|
||||
hx.Post(r.URL.Path),
|
||||
hx.Swap("outerHTML"),
|
||||
hx.Target("#generator"),
|
||||
Flex(
|
||||
Div(
|
||||
FormLabel(Text("Password length")),
|
||||
customFormInput(Type("number"), Name("length"), Value(ToString(length))),
|
||||
),
|
||||
Div(
|
||||
FormLabel(Text("Number Count")),
|
||||
customFormInput(Type("number"), Name("numbers"), Value(ToString(numbers))),
|
||||
),
|
||||
Div(
|
||||
FormLabel(Text("Symbol Count")),
|
||||
customFormInput(Type("number"), Name("symbols"), Value(ToString(symbols))),
|
||||
),
|
||||
),
|
||||
|
||||
ButtonUI(Text("Generate"), Type("submit")),
|
||||
),
|
||||
|
||||
Br(),
|
||||
|
||||
FormLabel(Text("Output")),
|
||||
customFormInput(
|
||||
Value(passwordOutput),
|
||||
),
|
||||
),
|
||||
).Render(w)
|
||||
}
|
||||
@@ -25,7 +25,7 @@ func VaultHandler(w http.ResponseWriter, r *http.Request) {
|
||||
margin-bottom: $5;
|
||||
}
|
||||
`),
|
||||
A(Href("/app/editor/add"), ButtonUI(Text("+ Add Item"))),
|
||||
A(Href("/app/editor/add"), ButtonUI(Text("+ Add Credentials"))),
|
||||
),
|
||||
HxLoad("/app/vault-hx"),
|
||||
).Render(w)
|
||||
|
||||
@@ -72,10 +72,58 @@ func VaultHxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func(entry entries.Secret) Node {
|
||||
return Tr(
|
||||
TdLeft(Text(entry.Description)),
|
||||
TdLeft(Text(entry.Username)),
|
||||
TdLeft(Text("********")),
|
||||
TdLeft(PageLink(security.SanitizationPolicy.Sanitize(entry.URL), Text(entry.URL), false)),
|
||||
TdCenter(A(Href("/app/editor/edit/" + entry.ID), ButtonUIOutline(Icon(ICON_PENCIL, 16)))),
|
||||
TdLeft(
|
||||
IfElse(entry.Username != "",
|
||||
Flex(
|
||||
P(Text(entry.Username)),
|
||||
Span(
|
||||
Title("Click to copy username"),
|
||||
InlineStyle("$me { cursor: pointer; }"),
|
||||
Icon(ICON_COPY, 16),
|
||||
),
|
||||
InlineScript(`
|
||||
let btn = me("span", me());
|
||||
let text = me("p", me());
|
||||
|
||||
btn.on("click", () => {
|
||||
navigator.clipboard.writeText(text.innerHTML);
|
||||
});
|
||||
`),
|
||||
),
|
||||
Text("---"),
|
||||
),
|
||||
),
|
||||
TdLeft(
|
||||
IfElse(entry.Password != "",
|
||||
Flex(
|
||||
P(Text("•••••••")),
|
||||
Input(Type("hidden"), Value(entry.Password)),
|
||||
Span(
|
||||
Class("copy"),
|
||||
Title("Click to copy password"),
|
||||
InlineStyle("$me { cursor: pointer; }"),
|
||||
Icon(ICON_COPY, 16),
|
||||
),
|
||||
InlineScript(`
|
||||
let copyBtn = me(".copy", me());
|
||||
let password = me("input", me()).value;
|
||||
|
||||
|
||||
copyBtn.on("click", () => {
|
||||
navigator.clipboard.writeText(password);
|
||||
});
|
||||
`),
|
||||
),
|
||||
Text("---"),
|
||||
),
|
||||
),
|
||||
TdLeft(
|
||||
IfElse(entry.URL != "",
|
||||
PageLink(security.SanitizationPolicy.Sanitize(entry.URL), Text(entry.URL), true),
|
||||
Text("---"),
|
||||
),
|
||||
),
|
||||
TdCenter(A(Href("/app/editor/edit/"+entry.ID), ButtonUIOutline(Icon(ICON_PENCIL, 16)))),
|
||||
)
|
||||
},
|
||||
nil,
|
||||
|
||||
@@ -27,7 +27,7 @@ var NavGroups = []NavGroup{
|
||||
{
|
||||
Title: "Tools",
|
||||
SubGroup: []NavGroup{
|
||||
{SectionId: LAYOUT_SECTION_TOOLS, Title: "Password Generator", URL: "/app/examples/forms"},
|
||||
{SectionId: LAYOUT_SECTION_TOOLS, Title: "Passkey Generator", URL: "/app/generator"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
22
ui/forms.go
22
ui/forms.go
@@ -1,6 +1,8 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
@@ -73,4 +75,24 @@ func FormTextarea(children ...Node) Node {
|
||||
|
||||
func FormLabel(children ...Node) Node {
|
||||
return Label(InlineStyle("$me { color: $color(neutral-900); font-size: var(--text-sm); } "), Group(children))
|
||||
}
|
||||
|
||||
func FormCheck(children ...Node) Node {
|
||||
return Input(
|
||||
InlineStyle(`
|
||||
$me {
|
||||
margin-left: $3;
|
||||
}
|
||||
`),
|
||||
Type("checkbox"),
|
||||
Group(children),
|
||||
)
|
||||
}
|
||||
|
||||
func FormSlider(min int, max int, children ...Node) Node {
|
||||
return Input(
|
||||
Type("range"),
|
||||
Min(strconv.Itoa(min)),
|
||||
Max(strconv.Itoa(max)),
|
||||
)
|
||||
}
|
||||
@@ -59,6 +59,13 @@ func Flex(n ...Node) Node {
|
||||
)
|
||||
}
|
||||
|
||||
func FlexLeftRight(n ...Node) Node {
|
||||
return Div(
|
||||
InlineStyle("$me { display: flex; align-items: center; flex-direction: row; gap: $3; justify-content: space-between; }"),
|
||||
Group(n),
|
||||
)
|
||||
}
|
||||
|
||||
func CardNoPadding(body ...Node) Node {
|
||||
return Div(
|
||||
InlineStyle(`
|
||||
@@ -109,6 +116,12 @@ func ToText(i interface{}) Node {
|
||||
}
|
||||
|
||||
// TEXT
|
||||
func Heading(text string) Node {
|
||||
return H1(
|
||||
InlineStyle("$me { font-weight: bold; font-size: var(--text-2xl); letter-spacing: var(--tracking-tight); color: $color(black); }"),
|
||||
Text(text),
|
||||
)
|
||||
}
|
||||
func PageLink(location string, display Node, newPage bool) Node {
|
||||
return A(
|
||||
Href(location),
|
||||
|
||||
@@ -38,6 +38,7 @@ const (
|
||||
ICON_X_DIALOG_CLOSE = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="1.4" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>`
|
||||
ICON_PENCIL = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pencil"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/></svg>`
|
||||
ICON_LOCK_KEYHOLE = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-lock-keyhole"><circle cx="12" cy="16" r="1"/><rect x="3" y="10" width="18" height="12" rx="2"/><path d="M7 10V7a5 5 0 0 1 10 0v3"/></svg>`
|
||||
ICON_COPY = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`
|
||||
|
||||
//material
|
||||
ICON_SEARCH = `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="3" stroke="currentColor" class="w-8 h-8 text-slate-600"><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" /></svg>`
|
||||
|
||||
20
vendor/github.com/sethvargo/go-diceware/LICENSE
generated
vendored
Normal file
20
vendor/github.com/sethvargo/go-diceware/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright 2017 Seth Vargo <seth@sethvargo.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
195
vendor/github.com/sethvargo/go-diceware/diceware/generate.go
generated
vendored
Normal file
195
vendor/github.com/sethvargo/go-diceware/diceware/generate.go
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
package diceware
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// sides is the number of sides on a die.
|
||||
var sides = big.NewInt(6)
|
||||
|
||||
var _ DicewareGenerator = (*Generator)(nil)
|
||||
|
||||
// Generator is the stateful generator which can be used to customize the word
|
||||
// list and other generation options.
|
||||
type Generator struct {
|
||||
wordList WordList
|
||||
randReader io.Reader
|
||||
}
|
||||
|
||||
// GeneratorInput is used as input to the NewGenerator function.
|
||||
type GeneratorInput struct {
|
||||
// WordList is the word list to use. There are built-in word lists like
|
||||
// WordListEffBig (default), WordListEffSmall, and WordListOriginal. You can
|
||||
// also bring your own word list by implementing the WordList interface.
|
||||
WordList WordList
|
||||
|
||||
// RandReader is an optional reader to use in place of the default
|
||||
// (crypto/rand.Reader), which can be used to generate repeatable sets of
|
||||
// words
|
||||
RandReader io.Reader
|
||||
}
|
||||
|
||||
// NewGenerator creates a new Generator from the specified configuration. If no
|
||||
// input is given, all the default values are used. This function is safe for
|
||||
// concurrent use.
|
||||
func NewGenerator(i *GeneratorInput) (*Generator, error) {
|
||||
if i == nil {
|
||||
i = new(GeneratorInput)
|
||||
}
|
||||
|
||||
if i.WordList == nil {
|
||||
i.WordList = WordListEffLarge()
|
||||
}
|
||||
|
||||
gen := &Generator{
|
||||
wordList: i.WordList,
|
||||
randReader: i.RandReader,
|
||||
}
|
||||
|
||||
if gen.randReader == nil {
|
||||
gen.randReader = rand.Reader
|
||||
}
|
||||
|
||||
return gen, nil
|
||||
}
|
||||
|
||||
// Generate generates a collection of diceware words, specified by the numWords
|
||||
// parameter.
|
||||
//
|
||||
// The algorithm is fast, but it's not designed to be performant, favoring
|
||||
// entropy over speed.
|
||||
//
|
||||
// This function is safe for concurrent use, but there is a possibility of
|
||||
// concurrent invocations generating overlapping words. To generate multiple
|
||||
// non-overlapping words, use a single invocation of the function and split the
|
||||
// resulting string list.
|
||||
func (g *Generator) Generate(numWords int) ([]string, error) {
|
||||
if typ, ok := g.wordList.(WordListNumWordser); ok {
|
||||
if l := typ.NumWords(); numWords > l {
|
||||
return nil, fmt.Errorf("number of requested words (%d) cannot exceed the size of the wordlist (%d)",
|
||||
numWords, l)
|
||||
}
|
||||
}
|
||||
|
||||
list := make([]string, 0, numWords)
|
||||
seen := make(map[string]struct{}, numWords)
|
||||
|
||||
for i := 0; i < numWords; i++ {
|
||||
n, err := g.RollWord(g.wordList.Digits())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
word := g.wordList.WordAt(n)
|
||||
if _, ok := seen[word]; ok {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
list = append(list, word)
|
||||
seen[word] = struct{}{}
|
||||
}
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// MustGenerate is the same as Generate, but panics on error.
|
||||
func (g *Generator) MustGenerate(numWords int) []string {
|
||||
list, err := g.Generate(numWords)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// Generate - see Generator.Generate for usage.
|
||||
func Generate(numWords int) ([]string, error) {
|
||||
gen, err := NewGenerator(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.Generate(numWords)
|
||||
}
|
||||
|
||||
// MustGenerate - see Generator.MustGenerate for usage.
|
||||
func MustGenerate(numWords int) []string {
|
||||
gen, err := NewGenerator(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return gen.MustGenerate(numWords)
|
||||
}
|
||||
|
||||
// GenerateWithWordList generates a list of the given number of words from the
|
||||
// given word list.
|
||||
func GenerateWithWordList(numWords int, wordList WordList) ([]string, error) {
|
||||
gen, err := NewGenerator(&GeneratorInput{
|
||||
WordList: wordList,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.Generate(numWords)
|
||||
}
|
||||
|
||||
// WordAt retrieves the word at the given index from EFF's large wordlist.
|
||||
//
|
||||
// Deprecated: Use WordList.WordAt instead.
|
||||
func WordAt(i int) string {
|
||||
return WordListEffLarge().WordAt(i)
|
||||
}
|
||||
|
||||
// RollDie rolls a single 6-sided die and returns a value between [1,6].
|
||||
//
|
||||
// Internally this creates a new Generator with a nil configuration and calls
|
||||
// Generator.RollDie.
|
||||
func RollDie() (int, error) {
|
||||
gen, err := NewGenerator(nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return gen.RollDie()
|
||||
}
|
||||
|
||||
// RollWord rolls and aggregates dice to represent one word in the list. The
|
||||
// result is the index of the word in the list.
|
||||
//
|
||||
// Internally this creates a new Generator with a nil configuration and calls
|
||||
// Generator.RollWord.
|
||||
func RollWord(d int) (int, error) {
|
||||
gen, err := NewGenerator(nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return gen.RollWord(d)
|
||||
}
|
||||
|
||||
// RollDie rolls a single 6-sided die and returns a value between [1,6].
|
||||
func (g *Generator) RollDie() (int, error) {
|
||||
r, err := rand.Int(g.randReader, sides)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to generate a random number: %w", err)
|
||||
}
|
||||
return int(r.Int64()) + 1, nil
|
||||
}
|
||||
|
||||
// RollWord rolls and aggregates dice to represent one word in the list. The
|
||||
// result is the index of the word in the list.
|
||||
func (g *Generator) RollWord(d int) (int, error) {
|
||||
var final int
|
||||
|
||||
for i := d; i > 0; i-- {
|
||||
res, err := g.RollDie()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
final += res * int(math.Pow(10, float64(i-1)))
|
||||
}
|
||||
|
||||
return final, nil
|
||||
}
|
||||
6
vendor/github.com/sethvargo/go-diceware/diceware/interface.go
generated
vendored
Normal file
6
vendor/github.com/sethvargo/go-diceware/diceware/interface.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package diceware
|
||||
|
||||
type DicewareGenerator interface {
|
||||
Generate(int) ([]string, error)
|
||||
MustGenerate(int) []string
|
||||
}
|
||||
38
vendor/github.com/sethvargo/go-diceware/diceware/mock.go
generated
vendored
Normal file
38
vendor/github.com/sethvargo/go-diceware/diceware/mock.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package diceware
|
||||
|
||||
var _ DicewareGenerator = (*mockGenerator)(nil)
|
||||
|
||||
type mockGenerator struct {
|
||||
result []string
|
||||
err error
|
||||
}
|
||||
|
||||
// NewMockGenerator creates a new generator that satisfies the DicewareGenerator
|
||||
// interface. If an error is provided, the error is returned. If a result if
|
||||
// provided, the result is always returned, regardless of what parameters are
|
||||
// passed into the Generate or MustGenerate methods.
|
||||
//
|
||||
// This function is most useful for tests where you want to have predicable
|
||||
// results for a transitive resource that depends on go-diceware.
|
||||
func NewMockGenerator(result []string, err error) *mockGenerator {
|
||||
return &mockGenerator{
|
||||
result: result,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Generate returns the mocked result or error.
|
||||
func (g *mockGenerator) Generate(int) ([]string, error) {
|
||||
if g.err != nil {
|
||||
return nil, g.err
|
||||
}
|
||||
return g.result, nil
|
||||
}
|
||||
|
||||
// MustGenerate returns the mocked result or panics if an error was given.
|
||||
func (g *mockGenerator) MustGenerate(int) []string {
|
||||
if g.err != nil {
|
||||
panic(g.err)
|
||||
}
|
||||
return g.result
|
||||
}
|
||||
42
vendor/github.com/sethvargo/go-diceware/diceware/word_list.go
generated
vendored
Normal file
42
vendor/github.com/sethvargo/go-diceware/diceware/word_list.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package diceware
|
||||
|
||||
// WordList is an interface that must be implemented to be considered a word
|
||||
// list for use in the diceware algorithm. This interface can be implemented by
|
||||
// other libraries.
|
||||
type WordList interface {
|
||||
// Digits is the number of digits for indexes in the word list. This
|
||||
// determines the number of dice rolls.
|
||||
Digits() int
|
||||
|
||||
// WordAt returns the word at the given integer in the word list.
|
||||
WordAt(int) string
|
||||
}
|
||||
|
||||
// WordListNumWordser is an auxiliary interface that returns the number of words
|
||||
// in the list. This is a separate interface for backwards compatibility.
|
||||
type WordListNumWordser interface {
|
||||
// NumWords returns the total number of words in the list.
|
||||
NumWords() int
|
||||
}
|
||||
|
||||
var (
|
||||
_ WordList = (*wordListInternal)(nil)
|
||||
_ WordListNumWordser = (*wordListInternal)(nil)
|
||||
)
|
||||
|
||||
type wordListInternal struct {
|
||||
digits int
|
||||
words map[int]string
|
||||
}
|
||||
|
||||
func (w *wordListInternal) Digits() int {
|
||||
return w.digits
|
||||
}
|
||||
|
||||
func (w *wordListInternal) WordAt(i int) string {
|
||||
return w.words[i]
|
||||
}
|
||||
|
||||
func (w *wordListInternal) NumWords() int {
|
||||
return len(w.words)
|
||||
}
|
||||
7794
vendor/github.com/sethvargo/go-diceware/diceware/word_list_eff_large.go
generated
vendored
Normal file
7794
vendor/github.com/sethvargo/go-diceware/diceware/word_list_eff_large.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1314
vendor/github.com/sethvargo/go-diceware/diceware/word_list_eff_small.go
generated
vendored
Normal file
1314
vendor/github.com/sethvargo/go-diceware/diceware/word_list_eff_small.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7789
vendor/github.com/sethvargo/go-diceware/diceware/word_list_original.go
generated
vendored
Normal file
7789
vendor/github.com/sethvargo/go-diceware/diceware/word_list_original.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
20
vendor/github.com/sethvargo/go-password/LICENSE
generated
vendored
Normal file
20
vendor/github.com/sethvargo/go-password/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright 2017 Seth Vargo <seth@sethvargo.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
262
vendor/github.com/sethvargo/go-password/password/generate.go
generated
vendored
Normal file
262
vendor/github.com/sethvargo/go-password/password/generate.go
generated
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
// Package password provides a library for generating high-entropy random
|
||||
// password strings via the crypto/rand package.
|
||||
//
|
||||
// res, err := Generate(64, 10, 10, false, false)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// log.Printf(res)
|
||||
//
|
||||
// Most functions are safe for concurrent use.
|
||||
package password
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Built-time checks that the generators implement the interface.
|
||||
var _ PasswordGenerator = (*Generator)(nil)
|
||||
|
||||
// PasswordGenerator is an interface that implements the Generate function. This
|
||||
// is useful for testing where you can pass this interface instead of a real
|
||||
// password generator to mock responses for predicability.
|
||||
type PasswordGenerator interface {
|
||||
Generate(int, int, int, bool, bool) (string, error)
|
||||
MustGenerate(int, int, int, bool, bool) string
|
||||
}
|
||||
|
||||
const (
|
||||
// LowerLetters is the list of lowercase letters.
|
||||
LowerLetters = "abcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
// UpperLetters is the list of uppercase letters.
|
||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
// Digits is the list of permitted digits.
|
||||
Digits = "0123456789"
|
||||
|
||||
// Symbols is the list of symbols.
|
||||
Symbols = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrExceedsTotalLength is the error returned with the number of digits and
|
||||
// symbols is greater than the total length.
|
||||
ErrExceedsTotalLength = errors.New("number of digits and symbols must be less than total length")
|
||||
|
||||
// ErrLettersExceedsAvailable is the error returned with the number of letters
|
||||
// exceeds the number of available letters and repeats are not allowed.
|
||||
ErrLettersExceedsAvailable = errors.New("number of letters exceeds available letters and repeats are not allowed")
|
||||
|
||||
// ErrDigitsExceedsAvailable is the error returned with the number of digits
|
||||
// exceeds the number of available digits and repeats are not allowed.
|
||||
ErrDigitsExceedsAvailable = errors.New("number of digits exceeds available digits and repeats are not allowed")
|
||||
|
||||
// ErrSymbolsExceedsAvailable is the error returned with the number of symbols
|
||||
// exceeds the number of available symbols and repeats are not allowed.
|
||||
ErrSymbolsExceedsAvailable = errors.New("number of symbols exceeds available symbols and repeats are not allowed")
|
||||
)
|
||||
|
||||
// Generator is the stateful generator which can be used to customize the list
|
||||
// of letters, digits, and/or symbols.
|
||||
type Generator struct {
|
||||
lowerLetters string
|
||||
upperLetters string
|
||||
digits string
|
||||
symbols string
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
// GeneratorInput is used as input to the NewGenerator function.
|
||||
type GeneratorInput struct {
|
||||
LowerLetters string
|
||||
UpperLetters string
|
||||
Digits string
|
||||
Symbols string
|
||||
Reader io.Reader // rand.Reader by default
|
||||
}
|
||||
|
||||
// NewGenerator creates a new Generator from the specified configuration. If no
|
||||
// input is given, all the default values are used. This function is safe for
|
||||
// concurrent use.
|
||||
func NewGenerator(i *GeneratorInput) (*Generator, error) {
|
||||
if i == nil {
|
||||
i = new(GeneratorInput)
|
||||
}
|
||||
|
||||
g := &Generator{
|
||||
lowerLetters: i.LowerLetters,
|
||||
upperLetters: i.UpperLetters,
|
||||
digits: i.Digits,
|
||||
symbols: i.Symbols,
|
||||
reader: i.Reader,
|
||||
}
|
||||
|
||||
if g.lowerLetters == "" {
|
||||
g.lowerLetters = LowerLetters
|
||||
}
|
||||
|
||||
if g.upperLetters == "" {
|
||||
g.upperLetters = UpperLetters
|
||||
}
|
||||
|
||||
if g.digits == "" {
|
||||
g.digits = Digits
|
||||
}
|
||||
|
||||
if g.symbols == "" {
|
||||
g.symbols = Symbols
|
||||
}
|
||||
|
||||
if g.reader == nil {
|
||||
g.reader = rand.Reader
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// Generate generates a password with the given requirements. length is the
|
||||
// total number of characters in the password. numDigits is the number of digits
|
||||
// to include in the result. numSymbols is the number of symbols to include in
|
||||
// the result. noUpper excludes uppercase letters from the results. allowRepeat
|
||||
// allows characters to repeat.
|
||||
//
|
||||
// The algorithm is fast, but it's not designed to be performant; it favors
|
||||
// entropy over speed. This function is safe for concurrent use.
|
||||
func (g *Generator) Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) {
|
||||
letters := g.lowerLetters
|
||||
if !noUpper {
|
||||
letters += g.upperLetters
|
||||
}
|
||||
|
||||
chars := length - numDigits - numSymbols
|
||||
if chars < 0 {
|
||||
return "", ErrExceedsTotalLength
|
||||
}
|
||||
|
||||
if !allowRepeat && chars > len(letters) {
|
||||
return "", ErrLettersExceedsAvailable
|
||||
}
|
||||
|
||||
if !allowRepeat && numDigits > len(g.digits) {
|
||||
return "", ErrDigitsExceedsAvailable
|
||||
}
|
||||
|
||||
if !allowRepeat && numSymbols > len(g.symbols) {
|
||||
return "", ErrSymbolsExceedsAvailable
|
||||
}
|
||||
|
||||
var result string
|
||||
|
||||
// Characters
|
||||
for i := 0; i < chars; i++ {
|
||||
ch, err := randomElement(g.reader, letters)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !allowRepeat && strings.Contains(result, ch) {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
result, err = randomInsert(g.reader, result, ch)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Digits
|
||||
for i := 0; i < numDigits; i++ {
|
||||
d, err := randomElement(g.reader, g.digits)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !allowRepeat && strings.Contains(result, d) {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
result, err = randomInsert(g.reader, result, d)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Symbols
|
||||
for i := 0; i < numSymbols; i++ {
|
||||
sym, err := randomElement(g.reader, g.symbols)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !allowRepeat && strings.Contains(result, sym) {
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
result, err = randomInsert(g.reader, result, sym)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// MustGenerate is the same as Generate, but panics on error.
|
||||
func (g *Generator) MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string {
|
||||
res, err := g.Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Generate is the package shortcut for Generator.Generate.
|
||||
func Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) {
|
||||
gen, err := NewGenerator(nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return gen.Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
|
||||
}
|
||||
|
||||
// MustGenerate is the package shortcut for Generator.MustGenerate.
|
||||
func MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string {
|
||||
res, err := Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// randomInsert randomly inserts the given value into the given string.
|
||||
func randomInsert(reader io.Reader, s, val string) (string, error) {
|
||||
if s == "" {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
n, err := rand.Int(reader, big.NewInt(int64(len(s)+1)))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate random integer: %w", err)
|
||||
}
|
||||
i := n.Int64()
|
||||
return s[0:i] + val + s[i:], nil
|
||||
}
|
||||
|
||||
// randomElement extracts a random element from the given string.
|
||||
func randomElement(reader io.Reader, s string) (string, error) {
|
||||
n, err := rand.Int(reader, big.NewInt(int64(len(s))))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate random integer: %w", err)
|
||||
}
|
||||
return string(s[n.Int64()]), nil
|
||||
}
|
||||
39
vendor/github.com/sethvargo/go-password/password/mock.go
generated
vendored
Normal file
39
vendor/github.com/sethvargo/go-password/password/mock.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package password
|
||||
|
||||
// Built-time checks that the generators implement the interface.
|
||||
var _ PasswordGenerator = (*mockGenerator)(nil)
|
||||
|
||||
type mockGenerator struct {
|
||||
result string
|
||||
err error
|
||||
}
|
||||
|
||||
// NewMockGenerator creates a new generator that satisfies the PasswordGenerator
|
||||
// interface. If an error is provided, the error is returned. If a result if
|
||||
// provided, the result is always returned, regardless of what parameters are
|
||||
// passed into the Generate or MustGenerate methods.
|
||||
//
|
||||
// This function is most useful for tests where you want to have predicable
|
||||
// results for a transitive resource that depends on go-password.
|
||||
func NewMockGenerator(result string, err error) *mockGenerator {
|
||||
return &mockGenerator{
|
||||
result: result,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Generate returns the mocked result or error.
|
||||
func (g *mockGenerator) Generate(int, int, int, bool, bool) (string, error) {
|
||||
if g.err != nil {
|
||||
return "", g.err
|
||||
}
|
||||
return g.result, nil
|
||||
}
|
||||
|
||||
// MustGenerate returns the mocked result or panics if an error was given.
|
||||
func (g *mockGenerator) MustGenerate(int, int, int, bool, bool) string {
|
||||
if g.err != nil {
|
||||
panic(g.err)
|
||||
}
|
||||
return g.result
|
||||
}
|
||||
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
@@ -78,6 +78,12 @@ github.com/pmezard/go-difflib/difflib
|
||||
# github.com/robfig/cron/v3 v3.0.1
|
||||
## explicit; go 1.12
|
||||
github.com/robfig/cron/v3
|
||||
# github.com/sethvargo/go-diceware v0.5.0
|
||||
## explicit; go 1.22
|
||||
github.com/sethvargo/go-diceware/diceware
|
||||
# github.com/sethvargo/go-password v0.3.1
|
||||
## explicit; go 1.21
|
||||
github.com/sethvargo/go-password/password
|
||||
# github.com/stretchr/testify v1.10.0
|
||||
## explicit; go 1.17
|
||||
github.com/stretchr/testify/assert
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user