Skip to content
This repository was archived by the owner on Feb 3, 2019. It is now read-only.
Open
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Inspired by [django-forms](https://docs.djangoproject.com/en/dev/topics/forms/)
## Install

```
go get github.com/bluele/gforms
go get github.com/votezilla/gforms
```

## Examples
Expand Down
Empty file added common_passwords.txt
Empty file.
226 changes: 226 additions & 0 deletions password_utility.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// Taken from https://github.com/briandowns/GoPasswordUtilities/edit/master/password_utility.go
//
// Copyright 2014 Brian J. Downs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Simple library for working with passwords in Go.
// All generated passwords are going to be a minimum of 8
// characters in length.
package gforms//GoPasswordUtilities

import (
"bufio"
"bytes"
"crypto/md5"
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"fmt"
"log"
"os"
"regexp"
)

const (
characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+,.?/:;{}[]~"
wordsLocation = "common_passwords.txt" // https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/10_million_password_list_top_1000000.txt
)

var (
commonPasswords = map[string]bool{}
passwordScores = map[int]string{
0: "Horrible",
1: "Weak",
2: "Medium",
3: "Strong",
4: "Very Strong"}
)

type Password struct {
Pass string
Length int
Score int
ContainsUpper bool
ContainsLower bool
ContainsNumber bool
ContainsSpecial bool
CommonPassword bool
}

type SaltConf struct {
Length int
}

// New is used when a user enters a password as well as the
// being called from the GeneratePassword function.
func New(password string) *Password {
return &Password{Pass: password, Length: len(password)}
}

// GeneratePassword will generate and return a password as a string and as a
// byte slice of the given length.
func GeneratePassword(length int) *Password {
passwordBuffer := new(bytes.Buffer)
randBytes := make([]byte, length)

if _, err := rand.Read(randBytes); err == nil {
for j := 0; j < length; j++ {
tmpIndex := int(randBytes[j]) % len(characters)
char := characters[tmpIndex]
passwordBuffer.WriteString(string(char))
}
}
return New(passwordBuffer.String())
}

// GenerateVeryStrongPassword will generate a "Very Strong" password.
func GenerateVeryStrongPassword(length int) *Password {
for {
p := GeneratePassword(length)
p.ProcessPassword()
if p.Score == 4 {
return p
}
}
}

// getRandomBytes will generate random bytes. This is for internal
// use in the library itself.
func getRandomBytes(length int) []byte {
randomData := make([]byte, length)
if _, err := rand.Read(randomData); err != nil {
log.Fatalf("%v\n", err)
}
return randomData
}

// MD5 sum for the given password. If a SaltConf
// pointer is given as a parameter a salt with the given
// length will be returned with it included in the hash.
func (p *Password) MD5(saltConf ...*SaltConf) ([16]byte, []byte) {
if len(saltConf) > 0 {
var saltLength int

for _, i := range saltConf[0:] {
saltLength = i.Length
}

salt := getRandomBytes(saltLength)
return md5.Sum([]byte(fmt.Sprintf("%s%x", p.Pass, salt))), salt
}
return md5.Sum([]byte(p.Pass)), nil
}

// SHA256 sum for the given password. If a SaltConf
// pointer is given as a parameter a salt with the given
// length will be returned with it included in the hash.
func (p *Password) SHA256(saltConf ...*SaltConf) ([32]byte, []byte) {
if len(saltConf) > 0 {
var saltLength int

for _, i := range saltConf[0:] {
saltLength = i.Length
}

salt := getRandomBytes(saltLength)
return sha256.Sum256([]byte(fmt.Sprintf("%s%x", p.Pass, salt))), salt
}
return sha256.Sum256([]byte(p.Pass)), nil
}

// SHA512 sum for the given password. If a SaltConf
// pointer is given as a parameter a salt with the given
// length will be returned with it included in the hash.
func (p *Password) SHA512(saltConf ...*SaltConf) ([64]byte, []byte) {
if len(saltConf) > 0 {
var saltLength int

for _, i := range saltConf[0:] {
saltLength = i.Length
}

salt := getRandomBytes(saltLength)
return sha512.Sum512([]byte(fmt.Sprintf("%s%x", p.Pass, salt))), salt
}
return sha512.Sum512([]byte(p.Pass)), nil
}

// GetLength will provide the length of the password. This method is
// being put on the password struct in case someone decides not to
// do a complexity check.
func (p *Password) GetLength() int {
return p.Length
}

// ProcessPassword will parse the password and populate the Password struct attributes.
func (p *Password) ProcessPassword() {
matchLower := regexp.MustCompile(`[a-z]`)
matchUpper := regexp.MustCompile(`[A-Z]`)
matchNumber := regexp.MustCompile(`[0-9]`)
matchSpecial := regexp.MustCompile(`[\!\@\#\$\%\^\&\*\(\\\)\-_\=\+\,\.\?\/\:\;\{\}\[\]~]`)

if p.Length < 8 {
p.Score = 0
return
}

if matchLower.MatchString(p.Pass) {
p.ContainsLower = true
p.Score++
}
if matchUpper.MatchString(p.Pass) {
p.ContainsUpper = true
p.Score++
}
if matchNumber.MatchString(p.Pass) {
p.ContainsNumber = true
p.Score++
}
if matchSpecial.MatchString(p.Pass) {
p.ContainsSpecial = true
p.Score++
}
if searchCommonPasswords(p.Pass) {
p.CommonPassword = true
p.Score = 0
}
}

func readCommonPasswordFile() {
file, err := os.Open(wordsLocation)
if err != nil {
log.Fatal(err)
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
commonPasswords[scanner.Text()] = true // does this work or do we need a .Set() ?
}
}

// searchCommonPasswords will search for match with exact list of common passwords
// to be installed.
func searchCommonPasswords(password string) bool {
return commonPasswords[password]
}

// ComplexityRating provides the rating for the password.
func (p *Password) ComplexityRating() string {
return passwordScores[p.Score]
}

func init() {
readCommonPasswordFile()
}
13 changes: 13 additions & 0 deletions selectwidget.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,19 @@ func SelectWidget(attrs map[string]string, mk SelectOptionsMaker) *selectWidget
return wg
}

// SelectWidget constructor which is easier use.
func SelectWidgetEasy(selectOptions [][2]string) *selectWidget {
return SelectWidget(
map[string]string{}, // attrs
// type SelectOptionsMaker func() SelectOptions
SelectOptionsMaker(func() SelectOptions {
ret := make(StringSelectOptions, len(selectOptions))
for i, option := range selectOptions {
ret[i] = []string{option[0], option[1], "false", "false"}
}
return ret
}))}

// Generate select-multiple and options field: <select multiple><option></option></select>
func SelectMultipleWidget(attrs map[string]string, mk SelectOptionsMaker) *selectWidget {
wg := SelectWidget(attrs, mk)
Expand Down
7 changes: 5 additions & 2 deletions templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ const defaultTemplates = `
{{define "BooleanTypeField"}}<input type="checkbox" name="{{.Field.GetName | html}}"{{if .Checked}} checked{{end}}>{{end}}
{{define "SimpleWidget"}}<input type="{{.Type | html}}" name="{{.Field.GetName | html}}" value="{{.Value | html}}"{{range $attr, $val := .Attrs}} {{$attr | html}}="{{$val | html}}"{{end}}></input>{{end}}
{{define "SelectWidget"}}<select {{if .Multiple }}multiple {{end}}name="{{.Field.GetName | html}}"{{range $attr, $val := .Attrs}}{{$attr | html}}="{{$val | html}}"{{end}}>
{{range $idx, $val := .Options}}<option value="{{$val.Value | html}}"{{if $val.Selected }} selected{{end}}{{if $val.Disabled}} disabled{{end}}>{{$val.Label | html}}</option>
{{range $idx, $val := .Options}}<option value="{{$val.Value | html}}"{{if or $val.Selected (eq $.Field.GetV.RawStr $val.Value)}} selected{{end}}{{if $val.Disabled}} disabled{{end}}>{{$val.Label | html}}</option>
{{end}}</select>{{end}}
{{define "RadioWidget"}}{{$name := .Field.GetName}}{{range $idx, $val := .Options}}<input type="radio" name="{{$name | html}}" value="{{$val.Value | html}}"{{if or $val.Checked (eq $.Field.GetV.RawStr $val.Value) }} checked{{end}}{{if $val.Disabled}} disabled{{end}}>{{$val.Label | html}}
{{end}}{{end}}
{{define "CheckboxMultipleWidget"}}{{$name := .Field.GetName}}{{range $idx, $val := .Options}}<input type="checkbox" name="{{$name | html}}" value="{{$val.Value | html}}"{{if $val.Checked}} checked{{end}}{{if $val.Disabled}} disabled{{end}}>{{$val.Label | html}}
{{define "CheckboxMultipleWidget"}}{{$name := .Field.GetName}}{{range $idx, $val := .Options}}<p class='checkbox'><input type="checkbox" name="{{$name | html}}" value="{{$val.Value | html}}"
{{- range $k, $v := $.Field.GetV.RawValue}}{{if eq $v $val.Value}} checked{{end}}{{end}}
{{- if $val.Checked}} checked{{end}}
{{- if $val.Disabled}} disabled{{end}}>{{$val.Label | html}}</p>
{{end}}{{end}}
`

Expand Down
Loading