Update validator function for URIs. Change example of entering a CA to a
root cert generation. Other minor documentation updates. Fix private key output in issue/sign.
This commit is contained in:
parent
f41a2e562a
commit
af3d6ced8e
|
@ -13,6 +13,11 @@
|
|||
"ImportPath": "github.com/armon/go-radix",
|
||||
"Rev": "fbd82e84e2b13651f3abc5ffd26b65ba71bc8f93"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/asaskevich/govalidator",
|
||||
"Comment": "v2-37-gedd46cd",
|
||||
"Rev": "edd46cdac249b001c7b7d88c6d43993ea875e8d8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aws/aws-sdk-go/aws",
|
||||
"Comment": "v0.10.4-18-gce51895",
|
||||
|
|
10
Godeps/_workspace/src/github.com/asaskevich/govalidator/.travis.yml
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/asaskevich/govalidator/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- bwatas@gmail.com
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Alex Saskevich
|
||||
|
||||
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.
|
281
Godeps/_workspace/src/github.com/asaskevich/govalidator/README.md
generated
vendored
Normal file
281
Godeps/_workspace/src/github.com/asaskevich/govalidator/README.md
generated
vendored
Normal file
|
@ -0,0 +1,281 @@
|
|||
govalidator
|
||||
===========
|
||||
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) [![Coverage Status](https://img.shields.io/coveralls/asaskevich/govalidator.svg)](https://coveralls.io/r/asaskevich/govalidator?branch=master) [![views](https://sourcegraph.com/api/repos/github.com/asaskevich/govalidator/.counters/views.png)](https://sourcegraph.com/github.com/asaskevich/govalidator)
|
||||
[![wercker status](https://app.wercker.com/status/1ec990b09ea86c910d5f08b0e02c6043/s "wercker status")](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043)
|
||||
[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator)
|
||||
|
||||
A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js).
|
||||
|
||||
#### Installation
|
||||
Make sure that Go is installed on your computer.
|
||||
Type the following command in your terminal:
|
||||
|
||||
go get github.com/asaskevich/govalidator
|
||||
|
||||
After it the package is ready to use.
|
||||
|
||||
#### Import package in your project
|
||||
Add following line in your `*.go` file:
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
```
|
||||
If you unhappy to use long `govalidator`, you can do something like this:
|
||||
```go
|
||||
import (
|
||||
valid "github.com/asaskevich/govalidator"
|
||||
)
|
||||
```
|
||||
|
||||
#### List of functions:
|
||||
```go
|
||||
func Abs(value float64) float64
|
||||
func BlackList(str, chars string) string
|
||||
func ByteLength(str string, params ...string) bool
|
||||
func StringLength(str string, params ...string) bool
|
||||
func CamelCaseToUnderscore(str string) string
|
||||
func Contains(str, substring string) bool
|
||||
func Count(array []interface{}, iterator ConditionIterator) int
|
||||
func Each(array []interface{}, iterator Iterator)
|
||||
func ErrorByField(e error, field string) string
|
||||
func Filter(array []interface{}, iterator ConditionIterator) []interface{}
|
||||
func Find(array []interface{}, iterator ConditionIterator) interface{}
|
||||
func GetLine(s string, index int) (string, error)
|
||||
func GetLines(s string) []string
|
||||
func InRange(value, left, right float64) bool
|
||||
func IsASCII(str string) bool
|
||||
func IsAlpha(str string) bool
|
||||
func IsAlphanumeric(str string) bool
|
||||
func IsBase64(str string) bool
|
||||
func IsByteLength(str string, min, max int) bool
|
||||
func IsCreditCard(str string) bool
|
||||
func IsDataURI(str string) bool
|
||||
func IsDivisibleBy(str, num string) bool
|
||||
func IsEmail(str string) bool
|
||||
func IsFilePath(str string) (bool, int)
|
||||
func IsFloat(str string) bool
|
||||
func IsFullWidth(str string) bool
|
||||
func IsHalfWidth(str string) bool
|
||||
func IsHexadecimal(str string) bool
|
||||
func IsHexcolor(str string) bool
|
||||
func IsIP(str string) bool
|
||||
func IsIPv4(str string) bool
|
||||
func IsIPv6(str string) bool
|
||||
func IsISBN(str string, version int) bool
|
||||
func IsISBN10(str string) bool
|
||||
func IsISBN13(str string) bool
|
||||
func IsISO3166Alpha2(str string) bool
|
||||
func IsISO3166Alpha3(str string) bool
|
||||
func IsInt(str string) bool
|
||||
func IsJSON(str string) bool
|
||||
func IsLatitude(str string) bool
|
||||
func IsLongitude(str string) bool
|
||||
func IsLowerCase(str string) bool
|
||||
func IsMAC(str string) bool
|
||||
func IsMongoID(str string) bool
|
||||
func IsMultibyte(str string) bool
|
||||
func IsNatural(value float64) bool
|
||||
func IsNegative(value float64) bool
|
||||
func IsNonNegative(value float64) bool
|
||||
func IsNonPositive(value float64) bool
|
||||
func IsNull(str string) bool
|
||||
func IsNumeric(str string) bool
|
||||
func IsPositive(value float64) bool
|
||||
func IsPrintableASCII(str string) bool
|
||||
func IsRGBcolor(str string) bool
|
||||
func IsRequestURI(rawurl string) bool
|
||||
func IsRequestURL(rawurl string) bool
|
||||
func IsSSN(str string) bool
|
||||
func IsURL(str string) bool
|
||||
func IsUTFDigit(str string) bool
|
||||
func IsUTFLetter(str string) bool
|
||||
func IsUTFLetterNumeric(str string) bool
|
||||
func IsUTFNumeric(str string) bool
|
||||
func IsUUID(str string) bool
|
||||
func IsUUIDv3(str string) bool
|
||||
func IsUUIDv4(str string) bool
|
||||
func IsUUIDv5(str string) bool
|
||||
func IsUpperCase(str string) bool
|
||||
func IsVariableWidth(str string) bool
|
||||
func IsWhole(value float64) bool
|
||||
func LeftTrim(str, chars string) string
|
||||
func Map(array []interface{}, iterator ResultIterator) []interface{}
|
||||
func Matches(str, pattern string) bool
|
||||
func NormalizeEmail(str string) (string, error)
|
||||
func RemoveTags(s string) string
|
||||
func ReplacePattern(str, pattern, replace string) string
|
||||
func Reverse(s string) string
|
||||
func RightTrim(str, chars string) string
|
||||
func SafeFileName(str string) string
|
||||
func Sign(value float64) float64
|
||||
func StripLow(str string, keepNewLines bool) string
|
||||
func ToBoolean(str string) (bool, error)
|
||||
func ToFloat(str string) (float64, error)
|
||||
func ToInt(str string) (int64, error)
|
||||
func ToJSON(obj interface{}) (string, error)
|
||||
func ToString(obj interface{}) string
|
||||
func Trim(str, chars string) string
|
||||
func Truncate(str string, length int, ending string) string
|
||||
func UnderscoreToCamelCase(s string) string
|
||||
func ValidateStruct(s interface{}) (bool, error)
|
||||
func WhiteList(str, chars string) string
|
||||
type ConditionIterator
|
||||
type Error
|
||||
func (e Error) Error() string
|
||||
type Errors
|
||||
func (es Errors) Error() string
|
||||
type ISO3166Entry
|
||||
type Iterator
|
||||
type ParamValidator
|
||||
type ResultIterator
|
||||
type UnsupportedTypeError
|
||||
func (e *UnsupportedTypeError) Error() string
|
||||
type Validator
|
||||
```
|
||||
|
||||
#### Examples
|
||||
###### IsURL
|
||||
```go
|
||||
println(govalidator.IsURL(`http://user@pass:domain.com/path/page`))
|
||||
```
|
||||
###### ToString
|
||||
```go
|
||||
type User struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
}
|
||||
|
||||
str, _ := govalidator.ToString(&User{"John", "Juan"})
|
||||
println(str)
|
||||
```
|
||||
###### Each, Map, Filter, Count for slices
|
||||
Each iterates over the slice/array and calls Iterator for every item
|
||||
```go
|
||||
data := []interface{}{1, 2, 3, 4, 5}
|
||||
var fn govalidator.Iterator = func(value interface{}, index int) {
|
||||
println(value.(int))
|
||||
}
|
||||
govalidator.Each(data, fn)
|
||||
```
|
||||
```go
|
||||
data := []interface{}{1, 2, 3, 4, 5}
|
||||
var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} {
|
||||
return value.(int) * 3
|
||||
}
|
||||
_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
|
||||
```
|
||||
```go
|
||||
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
var fn govalidator.ConditionIterator = func(value interface{}, index int) bool {
|
||||
return value.(int)%2 == 0
|
||||
}
|
||||
_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
|
||||
_ = govalidator.Count(data, fn) // result = 5
|
||||
```
|
||||
###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2)
|
||||
If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this:
|
||||
```go
|
||||
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||
return str == "duck"
|
||||
})
|
||||
```
|
||||
Here is a list of available validators for struct fields (validator - used function):
|
||||
```go
|
||||
"alpha": IsAlpha,
|
||||
"alphanum": IsAlphanumeric,
|
||||
"ascii": IsASCII,
|
||||
"base64": IsBase64,
|
||||
"creditcard": IsCreditCard,
|
||||
"datauri": IsDataURI,
|
||||
"email": IsEmail,
|
||||
"float": IsFloat,
|
||||
"fullwidth": IsFullWidth,
|
||||
"halfwidth": IsHalfWidth,
|
||||
"hexadecimal": IsHexadecimal,
|
||||
"hexcolor": IsHexcolor,
|
||||
"int": IsInt,
|
||||
"ip": IsIP,
|
||||
"ipv4": IsIPv4,
|
||||
"ipv6": IsIPv6,
|
||||
"isbn10": IsISBN10,
|
||||
"isbn13": IsISBN13,
|
||||
"json": IsJSON,
|
||||
"latitude": IsLatitude,
|
||||
"longitude": IsLongitude,
|
||||
"lowercase": IsLowerCase,
|
||||
"mac": IsMAC,
|
||||
"multibyte": IsMultibyte,
|
||||
"null": IsNull,
|
||||
"numeric": IsNumeric,
|
||||
"printableascii": IsPrintableASCII,
|
||||
"requri": IsRequestURI,
|
||||
"requrl": IsRequestURL,
|
||||
"rgbcolor": IsRGBcolor,
|
||||
"ssn": IsSSN
|
||||
"uppercase": IsUpperCase,
|
||||
"url": IsURL,
|
||||
"utfdigit": IsUTFDigit,
|
||||
"utfletter": IsUTFLetter,
|
||||
"utfletternum": IsUTFLetterNumeric,
|
||||
"utfnumeric": IsUTFNumeric,
|
||||
"uuid": IsUUID,
|
||||
"uuidv3": IsUUIDv3,
|
||||
"uuidv4": IsUUIDv4,
|
||||
"uuidv5": IsUUIDv5,
|
||||
"variablewidth": IsVariableWidth,
|
||||
```
|
||||
Validators with parameters
|
||||
|
||||
```go
|
||||
"length(min|max)": ByteLength,
|
||||
```
|
||||
|
||||
And here is small example of usage:
|
||||
```go
|
||||
type Post struct {
|
||||
Title string `valid:"alphanum,required"`
|
||||
Message string `valid:"duck,ascii"`
|
||||
AuthorIP string `valid:"ipv4"`
|
||||
Date string `valid:"-"`
|
||||
}
|
||||
post := &Post{
|
||||
Title: "My Example Post",
|
||||
Message: "duck",
|
||||
AuthorIP: "123.234.54.3",
|
||||
}
|
||||
|
||||
// Add your own struct validation tags
|
||||
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||
return str == "duck"
|
||||
})
|
||||
|
||||
result, err := govalidator.ValidateStruct(post)
|
||||
if err != nil {
|
||||
println("error: " + err.Error())
|
||||
}
|
||||
println(result)
|
||||
```
|
||||
###### WhiteList
|
||||
```go
|
||||
// Remove all characters from string ignoring characters between "a" and "z"
|
||||
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
|
||||
```
|
||||
|
||||
#### Notes
|
||||
Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator).
|
||||
Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator).
|
||||
|
||||
#### Support
|
||||
If you do have a contribution for the package feel free to put up a Pull Request or open Issue.
|
||||
|
||||
#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors)
|
||||
* [Attila Oláh](https://github.com/attilaolah)
|
||||
* [Daniel Korner](https://github.com/Dadie)
|
||||
* [Steven Wilkin](https://github.com/stevenwilkin)
|
||||
* [Deiwin Sarjas](https://github.com/deiwin)
|
||||
* [Noah Shibley](https://github.com/slugmobile)
|
||||
* [Nathan Davies](https://github.com/nathj07)
|
||||
* [Matt Sanford](https://github.com/mzsanford)
|
||||
* [Simon ccl1115](https://github.com/ccl1115)
|
||||
|
||||
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/asaskevich/govalidator/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package govalidator
|
||||
|
||||
// Iterator is the function that accepts element of slice/array and its index
|
||||
type Iterator func(interface{}, int)
|
||||
|
||||
// ResultIterator is the function that accepts element of slice/array and its index and returns any result
|
||||
type ResultIterator func(interface{}, int) interface{}
|
||||
|
||||
// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean
|
||||
type ConditionIterator func(interface{}, int) bool
|
||||
|
||||
// Each iterates over the slice and apply Iterator to every item
|
||||
func Each(array []interface{}, iterator Iterator) {
|
||||
for index, data := range array {
|
||||
iterator(data, index)
|
||||
}
|
||||
}
|
||||
|
||||
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
|
||||
func Map(array []interface{}, iterator ResultIterator) []interface{} {
|
||||
var result []interface{} = make([]interface{}, len(array))
|
||||
for index, data := range array {
|
||||
result[index] = iterator(data, index)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise.
|
||||
func Find(array []interface{}, iterator ConditionIterator) interface{} {
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
return data
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
|
||||
func Filter(array []interface{}, iterator ConditionIterator) []interface{} {
|
||||
var result []interface{} = make([]interface{}, 0)
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
result = append(result, data)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator.
|
||||
func Count(array []interface{}, iterator ConditionIterator) int {
|
||||
count := 0
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
count = count + 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
49
Godeps/_workspace/src/github.com/asaskevich/govalidator/converter.go
generated
vendored
Normal file
49
Godeps/_workspace/src/github.com/asaskevich/govalidator/converter.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package govalidator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ToString convert the input to a string.
|
||||
func ToString(obj interface{}) string {
|
||||
res := fmt.Sprintf("%v", obj)
|
||||
return string(res)
|
||||
}
|
||||
|
||||
// ToJSON convert the input to a valid JSON string
|
||||
func ToJSON(obj interface{}) (string, error) {
|
||||
res, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
res = []byte("")
|
||||
}
|
||||
return string(res), err
|
||||
}
|
||||
|
||||
// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
|
||||
func ToFloat(str string) (float64, error) {
|
||||
res, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
res = 0.0
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ToInt convert the input string to an integer, or 0 if the input is not an integer.
|
||||
func ToInt(str string) (int64, error) {
|
||||
res, err := strconv.ParseInt(str, 0, 64)
|
||||
if err != nil {
|
||||
res = 0
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ToBoolean convert the input string to a boolean.
|
||||
func ToBoolean(str string) (bool, error) {
|
||||
res, err := strconv.ParseBool(str)
|
||||
if err != nil {
|
||||
res = false
|
||||
}
|
||||
return res, err
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package govalidator
|
||||
|
||||
type Errors []error
|
||||
|
||||
func (es Errors) Errors() []error {
|
||||
return es
|
||||
}
|
||||
|
||||
func (es Errors) Error() string {
|
||||
var err string
|
||||
for _, e := range es {
|
||||
err += e.Error() + ";"
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Name string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return e.Name + ": " + e.Err.Error()
|
||||
}
|
57
Godeps/_workspace/src/github.com/asaskevich/govalidator/numerics.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/asaskevich/govalidator/numerics.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package govalidator
|
||||
|
||||
import "math"
|
||||
|
||||
// Abs returns absolute value of number
|
||||
func Abs(value float64) float64 {
|
||||
return value * Sign(value)
|
||||
}
|
||||
|
||||
// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
|
||||
func Sign(value float64) float64 {
|
||||
if value > 0 {
|
||||
return 1
|
||||
} else if value < 0 {
|
||||
return -1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// IsNegative returns true if value < 0
|
||||
func IsNegative(value float64) bool {
|
||||
return value < 0
|
||||
}
|
||||
|
||||
// IsPositive returns true if value > 0
|
||||
func IsPositive(value float64) bool {
|
||||
return value > 0
|
||||
}
|
||||
|
||||
// IsNonNegative returns true if value >= 0
|
||||
func IsNonNegative(value float64) bool {
|
||||
return value >= 0
|
||||
}
|
||||
|
||||
// IsNonPositive returns true if value <= 0
|
||||
func IsNonPositive(value float64) bool {
|
||||
return value <= 0
|
||||
}
|
||||
|
||||
// InRange returns true if value lies between left and right border
|
||||
func InRange(value, left, right float64) bool {
|
||||
if left > right {
|
||||
left, right = right, left
|
||||
}
|
||||
return value >= left && value <= right
|
||||
}
|
||||
|
||||
// IsWhole returns true if value is whole number
|
||||
func IsWhole(value float64) bool {
|
||||
return Abs(math.Remainder(value, 1)) == 0
|
||||
}
|
||||
|
||||
// IsNatural returns true if value is natural number (positive and whole)
|
||||
func IsNatural(value float64) bool {
|
||||
return IsWhole(value) && IsPositive(value)
|
||||
}
|
79
Godeps/_workspace/src/github.com/asaskevich/govalidator/patterns.go
generated
vendored
Normal file
79
Godeps/_workspace/src/github.com/asaskevich/govalidator/patterns.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
package govalidator
|
||||
|
||||
import "regexp"
|
||||
|
||||
// Basic regular expressions for validating strings
|
||||
const (
|
||||
Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
|
||||
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$"
|
||||
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$"
|
||||
ISBN13 string = "^(?:[0-9]{13})$"
|
||||
UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
Alpha string = "^[a-zA-Z]+$"
|
||||
Alphanumeric string = "^[a-zA-Z0-9]+$"
|
||||
Numeric string = "^[-+]?[0-9]+$"
|
||||
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
|
||||
Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$"
|
||||
Hexadecimal string = "^[0-9a-fA-F]+$"
|
||||
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
|
||||
RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
|
||||
ASCII string = "^[\x00-\x7F]+$"
|
||||
Multibyte string = "[^\x00-\x7F]"
|
||||
FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||
HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
|
||||
PrintableASCII string = "^[\x20-\x7E]+$"
|
||||
DataURI string = "^data:.+\\/(.+);base64$"
|
||||
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
|
||||
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
|
||||
URL string = `^((ftp|https?):\/\/)?(\S+(:\S*)?@)?((([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(([a-zA-Z0-9]+([-\.][a-zA-Z0-9]+)*)|((www\.)?))?(([a-z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-z\x{00a1}-\x{ffff}]{2,}))?))(:(\d{1,5}))?((\/|\?|#)[^\s]*)?$`
|
||||
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
|
||||
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
|
||||
UnixPath string = `^((?:\/[a-zA-Z0-9\.\:]+(?:_[a-zA-Z0-9\:\.]+)*(?:\-[\:a-zA-Z0-9\.]+)*)+\/?)$`
|
||||
tagName string = "valid"
|
||||
)
|
||||
|
||||
// Used by IsFilePath func
|
||||
const (
|
||||
// Unknown is unresolved OS type
|
||||
Unknown = iota
|
||||
// Win is Windows type
|
||||
Win
|
||||
// Unix is *nix OS types
|
||||
Unix
|
||||
)
|
||||
|
||||
var (
|
||||
rxEmail = regexp.MustCompile(Email)
|
||||
rxCreditCard = regexp.MustCompile(CreditCard)
|
||||
rxISBN10 = regexp.MustCompile(ISBN10)
|
||||
rxISBN13 = regexp.MustCompile(ISBN13)
|
||||
rxUUID3 = regexp.MustCompile(UUID3)
|
||||
rxUUID4 = regexp.MustCompile(UUID4)
|
||||
rxUUID5 = regexp.MustCompile(UUID5)
|
||||
rxUUID = regexp.MustCompile(UUID)
|
||||
rxAlpha = regexp.MustCompile(Alpha)
|
||||
rxAlphanumeric = regexp.MustCompile(Alphanumeric)
|
||||
rxNumeric = regexp.MustCompile(Numeric)
|
||||
rxInt = regexp.MustCompile(Int)
|
||||
rxFloat = regexp.MustCompile(Float)
|
||||
rxHexadecimal = regexp.MustCompile(Hexadecimal)
|
||||
rxHexcolor = regexp.MustCompile(Hexcolor)
|
||||
rxRGBcolor = regexp.MustCompile(RGBcolor)
|
||||
rxASCII = regexp.MustCompile(ASCII)
|
||||
rxPrintableASCII = regexp.MustCompile(PrintableASCII)
|
||||
rxMultibyte = regexp.MustCompile(Multibyte)
|
||||
rxFullWidth = regexp.MustCompile(FullWidth)
|
||||
rxHalfWidth = regexp.MustCompile(HalfWidth)
|
||||
rxBase64 = regexp.MustCompile(Base64)
|
||||
rxDataURI = regexp.MustCompile(DataURI)
|
||||
rxLatitude = regexp.MustCompile(Latitude)
|
||||
rxLongitude = regexp.MustCompile(Longitude)
|
||||
rxURL = regexp.MustCompile(URL)
|
||||
rxSSN = regexp.MustCompile(SSN)
|
||||
rxWinPath = regexp.MustCompile(WinPath)
|
||||
rxUnixPath = regexp.MustCompile(UnixPath)
|
||||
)
|
|
@ -0,0 +1,349 @@
|
|||
package govalidator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Validator is a wrapper for a validator function that returns bool and accepts string.
|
||||
type Validator func(str string) bool
|
||||
|
||||
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
|
||||
type CustomTypeValidator func(i interface{}) bool
|
||||
|
||||
// ParamValidator is a wrapper for validator functions that accepts additional parameters.
|
||||
type ParamValidator func(str string, params ...string) bool
|
||||
type tagOptions []string
|
||||
|
||||
// UnsupportedTypeError is a wrapper for reflect.Type
|
||||
type UnsupportedTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
|
||||
// It implements the methods to sort by string.
|
||||
type stringValues []reflect.Value
|
||||
|
||||
// ParamTagMap is a map of functions accept variants parameters
|
||||
var ParamTagMap = map[string]ParamValidator{
|
||||
"length": ByteLength,
|
||||
"stringlength": StringLength,
|
||||
}
|
||||
|
||||
var ParamTagRegexMap = map[string]*regexp.Regexp{
|
||||
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
|
||||
}
|
||||
|
||||
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
|
||||
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
|
||||
// `type UUID [16]byte` (this would be handled as an array of bytes).
|
||||
var CustomTypeTagMap = map[string]CustomTypeValidator{}
|
||||
|
||||
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
|
||||
var TagMap = map[string]Validator{
|
||||
"email": IsEmail,
|
||||
"url": IsURL,
|
||||
"requrl": IsRequestURL,
|
||||
"requri": IsRequestURI,
|
||||
"alpha": IsAlpha,
|
||||
"utfletter": IsUTFLetter,
|
||||
"alphanum": IsAlphanumeric,
|
||||
"utfletternum": IsUTFLetterNumeric,
|
||||
"numeric": IsNumeric,
|
||||
"utfnumeric": IsUTFNumeric,
|
||||
"utfdigit": IsUTFDigit,
|
||||
"hexadecimal": IsHexadecimal,
|
||||
"hexcolor": IsHexcolor,
|
||||
"rgbcolor": IsRGBcolor,
|
||||
"lowercase": IsLowerCase,
|
||||
"uppercase": IsUpperCase,
|
||||
"int": IsInt,
|
||||
"float": IsFloat,
|
||||
"null": IsNull,
|
||||
"uuid": IsUUID,
|
||||
"uuidv3": IsUUIDv3,
|
||||
"uuidv4": IsUUIDv4,
|
||||
"uuidv5": IsUUIDv5,
|
||||
"creditcard": IsCreditCard,
|
||||
"isbn10": IsISBN10,
|
||||
"isbn13": IsISBN13,
|
||||
"json": IsJSON,
|
||||
"multibyte": IsMultibyte,
|
||||
"ascii": IsASCII,
|
||||
"printableascii": IsPrintableASCII,
|
||||
"fullwidth": IsFullWidth,
|
||||
"halfwidth": IsHalfWidth,
|
||||
"variablewidth": IsVariableWidth,
|
||||
"base64": IsBase64,
|
||||
"datauri": IsDataURI,
|
||||
"ip": IsIP,
|
||||
"ipv4": IsIPv4,
|
||||
"ipv6": IsIPv6,
|
||||
"mac": IsMAC,
|
||||
"latitude": IsLatitude,
|
||||
"longitude": IsLongitude,
|
||||
"ssn": IsSSN,
|
||||
}
|
||||
|
||||
// ISO3166Entry stores country codes
|
||||
type ISO3166Entry struct {
|
||||
EnglishShortName string
|
||||
FrenchShortName string
|
||||
Alpha2Code string
|
||||
Alpha3Code string
|
||||
Numeric string
|
||||
}
|
||||
|
||||
//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
|
||||
var ISO3166List = []ISO3166Entry{
|
||||
{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"},
|
||||
{"Albania", "Albanie (l')", "AL", "ALB", "008"},
|
||||
{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"},
|
||||
{"Algeria", "Algérie (l')", "DZ", "DZA", "012"},
|
||||
{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"},
|
||||
{"Andorra", "Andorre (l')", "AD", "AND", "020"},
|
||||
{"Angola", "Angola (l')", "AO", "AGO", "024"},
|
||||
{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"},
|
||||
{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"},
|
||||
{"Argentina", "Argentine (l')", "AR", "ARG", "032"},
|
||||
{"Australia", "Australie (l')", "AU", "AUS", "036"},
|
||||
{"Austria", "Autriche (l')", "AT", "AUT", "040"},
|
||||
{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"},
|
||||
{"Bahrain", "Bahreïn", "BH", "BHR", "048"},
|
||||
{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"},
|
||||
{"Armenia", "Arménie (l')", "AM", "ARM", "051"},
|
||||
{"Barbados", "Barbade (la)", "BB", "BRB", "052"},
|
||||
{"Belgium", "Belgique (la)", "BE", "BEL", "056"},
|
||||
{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"},
|
||||
{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"},
|
||||
{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"},
|
||||
{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"},
|
||||
{"Botswana", "Botswana (le)", "BW", "BWA", "072"},
|
||||
{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"},
|
||||
{"Brazil", "Brésil (le)", "BR", "BRA", "076"},
|
||||
{"Belize", "Belize (le)", "BZ", "BLZ", "084"},
|
||||
{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"},
|
||||
{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"},
|
||||
{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"},
|
||||
{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"},
|
||||
{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"},
|
||||
{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"},
|
||||
{"Burundi", "Burundi (le)", "BI", "BDI", "108"},
|
||||
{"Belarus", "Bélarus (le)", "BY", "BLR", "112"},
|
||||
{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"},
|
||||
{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"},
|
||||
{"Canada", "Canada (le)", "CA", "CAN", "124"},
|
||||
{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"},
|
||||
{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"},
|
||||
{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"},
|
||||
{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"},
|
||||
{"Chad", "Tchad (le)", "TD", "TCD", "148"},
|
||||
{"Chile", "Chili (le)", "CL", "CHL", "152"},
|
||||
{"China", "Chine (la)", "CN", "CHN", "156"},
|
||||
{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"},
|
||||
{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"},
|
||||
{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"},
|
||||
{"Colombia", "Colombie (la)", "CO", "COL", "170"},
|
||||
{"Comoros (the)", "Comores (les)", "KM", "COM", "174"},
|
||||
{"Mayotte", "Mayotte", "YT", "MYT", "175"},
|
||||
{"Congo (the)", "Congo (le)", "CG", "COG", "178"},
|
||||
{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"},
|
||||
{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"},
|
||||
{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"},
|
||||
{"Croatia", "Croatie (la)", "HR", "HRV", "191"},
|
||||
{"Cuba", "Cuba", "CU", "CUB", "192"},
|
||||
{"Cyprus", "Chypre", "CY", "CYP", "196"},
|
||||
{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"},
|
||||
{"Benin", "Bénin (le)", "BJ", "BEN", "204"},
|
||||
{"Denmark", "Danemark (le)", "DK", "DNK", "208"},
|
||||
{"Dominica", "Dominique (la)", "DM", "DMA", "212"},
|
||||
{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"},
|
||||
{"Ecuador", "Équateur (l')", "EC", "ECU", "218"},
|
||||
{"El Salvador", "El Salvador", "SV", "SLV", "222"},
|
||||
{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"},
|
||||
{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"},
|
||||
{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"},
|
||||
{"Estonia", "Estonie (l')", "EE", "EST", "233"},
|
||||
{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"},
|
||||
{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"},
|
||||
{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"},
|
||||
{"Fiji", "Fidji (les)", "FJ", "FJI", "242"},
|
||||
{"Finland", "Finlande (la)", "FI", "FIN", "246"},
|
||||
{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"},
|
||||
{"France", "France (la)", "FR", "FRA", "250"},
|
||||
{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"},
|
||||
{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"},
|
||||
{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"},
|
||||
{"Djibouti", "Djibouti", "DJ", "DJI", "262"},
|
||||
{"Gabon", "Gabon (le)", "GA", "GAB", "266"},
|
||||
{"Georgia", "Géorgie (la)", "GE", "GEO", "268"},
|
||||
{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"},
|
||||
{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"},
|
||||
{"Germany", "Allemagne (l')", "DE", "DEU", "276"},
|
||||
{"Ghana", "Ghana (le)", "GH", "GHA", "288"},
|
||||
{"Gibraltar", "Gibraltar", "GI", "GIB", "292"},
|
||||
{"Kiribati", "Kiribati", "KI", "KIR", "296"},
|
||||
{"Greece", "Grèce (la)", "GR", "GRC", "300"},
|
||||
{"Greenland", "Groenland (le)", "GL", "GRL", "304"},
|
||||
{"Grenada", "Grenade (la)", "GD", "GRD", "308"},
|
||||
{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"},
|
||||
{"Guam", "Guam", "GU", "GUM", "316"},
|
||||
{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"},
|
||||
{"Guinea", "Guinée (la)", "GN", "GIN", "324"},
|
||||
{"Guyana", "Guyana (le)", "GY", "GUY", "328"},
|
||||
{"Haiti", "Haïti", "HT", "HTI", "332"},
|
||||
{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"},
|
||||
{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"},
|
||||
{"Honduras", "Honduras (le)", "HN", "HND", "340"},
|
||||
{"Hong Kong", "Hong Kong", "HK", "HKG", "344"},
|
||||
{"Hungary", "Hongrie (la)", "HU", "HUN", "348"},
|
||||
{"Iceland", "Islande (l')", "IS", "ISL", "352"},
|
||||
{"India", "Inde (l')", "IN", "IND", "356"},
|
||||
{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"},
|
||||
{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"},
|
||||
{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"},
|
||||
{"Ireland", "Irlande (l')", "IE", "IRL", "372"},
|
||||
{"Israel", "Israël", "IL", "ISR", "376"},
|
||||
{"Italy", "Italie (l')", "IT", "ITA", "380"},
|
||||
{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"},
|
||||
{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"},
|
||||
{"Japan", "Japon (le)", "JP", "JPN", "392"},
|
||||
{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"},
|
||||
{"Jordan", "Jordanie (la)", "JO", "JOR", "400"},
|
||||
{"Kenya", "Kenya (le)", "KE", "KEN", "404"},
|
||||
{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"},
|
||||
{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"},
|
||||
{"Kuwait", "Koweït (le)", "KW", "KWT", "414"},
|
||||
{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"},
|
||||
{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"},
|
||||
{"Lebanon", "Liban (le)", "LB", "LBN", "422"},
|
||||
{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"},
|
||||
{"Latvia", "Lettonie (la)", "LV", "LVA", "428"},
|
||||
{"Liberia", "Libéria (le)", "LR", "LBR", "430"},
|
||||
{"Libya", "Libye (la)", "LY", "LBY", "434"},
|
||||
{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"},
|
||||
{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"},
|
||||
{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"},
|
||||
{"Macao", "Macao", "MO", "MAC", "446"},
|
||||
{"Madagascar", "Madagascar", "MG", "MDG", "450"},
|
||||
{"Malawi", "Malawi (le)", "MW", "MWI", "454"},
|
||||
{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"},
|
||||
{"Maldives", "Maldives (les)", "MV", "MDV", "462"},
|
||||
{"Mali", "Mali (le)", "ML", "MLI", "466"},
|
||||
{"Malta", "Malte", "MT", "MLT", "470"},
|
||||
{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"},
|
||||
{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"},
|
||||
{"Mauritius", "Maurice", "MU", "MUS", "480"},
|
||||
{"Mexico", "Mexique (le)", "MX", "MEX", "484"},
|
||||
{"Monaco", "Monaco", "MC", "MCO", "492"},
|
||||
{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"},
|
||||
{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"},
|
||||
{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"},
|
||||
{"Montserrat", "Montserrat", "MS", "MSR", "500"},
|
||||
{"Morocco", "Maroc (le)", "MA", "MAR", "504"},
|
||||
{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"},
|
||||
{"Oman", "Oman", "OM", "OMN", "512"},
|
||||
{"Namibia", "Namibie (la)", "NA", "NAM", "516"},
|
||||
{"Nauru", "Nauru", "NR", "NRU", "520"},
|
||||
{"Nepal", "Népal (le)", "NP", "NPL", "524"},
|
||||
{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"},
|
||||
{"Curaçao", "Curaçao", "CW", "CUW", "531"},
|
||||
{"Aruba", "Aruba", "AW", "ABW", "533"},
|
||||
{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"},
|
||||
{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"},
|
||||
{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"},
|
||||
{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"},
|
||||
{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"},
|
||||
{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"},
|
||||
{"Niger (the)", "Niger (le)", "NE", "NER", "562"},
|
||||
{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"},
|
||||
{"Niue", "Niue", "NU", "NIU", "570"},
|
||||
{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"},
|
||||
{"Norway", "Norvège (la)", "NO", "NOR", "578"},
|
||||
{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"},
|
||||
{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"},
|
||||
{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"},
|
||||
{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"},
|
||||
{"Palau", "Palaos (les)", "PW", "PLW", "585"},
|
||||
{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"},
|
||||
{"Panama", "Panama (le)", "PA", "PAN", "591"},
|
||||
{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"},
|
||||
{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"},
|
||||
{"Peru", "Pérou (le)", "PE", "PER", "604"},
|
||||
{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"},
|
||||
{"Pitcairn", "Pitcairn", "PN", "PCN", "612"},
|
||||
{"Poland", "Pologne (la)", "PL", "POL", "616"},
|
||||
{"Portugal", "Portugal (le)", "PT", "PRT", "620"},
|
||||
{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"},
|
||||
{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"},
|
||||
{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"},
|
||||
{"Qatar", "Qatar (le)", "QA", "QAT", "634"},
|
||||
{"Réunion", "Réunion (La)", "RE", "REU", "638"},
|
||||
{"Romania", "Roumanie (la)", "RO", "ROU", "642"},
|
||||
{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"},
|
||||
{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"},
|
||||
{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"},
|
||||
{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"},
|
||||
{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"},
|
||||
{"Anguilla", "Anguilla", "AI", "AIA", "660"},
|
||||
{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"},
|
||||
{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"},
|
||||
{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"},
|
||||
{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"},
|
||||
{"San Marino", "Saint-Marin", "SM", "SMR", "674"},
|
||||
{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"},
|
||||
{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"},
|
||||
{"Senegal", "Sénégal (le)", "SN", "SEN", "686"},
|
||||
{"Serbia", "Serbie (la)", "RS", "SRB", "688"},
|
||||
{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"},
|
||||
{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"},
|
||||
{"Singapore", "Singapour", "SG", "SGP", "702"},
|
||||
{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"},
|
||||
{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"},
|
||||
{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"},
|
||||
{"Somalia", "Somalie (la)", "SO", "SOM", "706"},
|
||||
{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"},
|
||||
{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"},
|
||||
{"Spain", "Espagne (l')", "ES", "ESP", "724"},
|
||||
{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"},
|
||||
{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"},
|
||||
{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"},
|
||||
{"Suriname", "Suriname (le)", "SR", "SUR", "740"},
|
||||
{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"},
|
||||
{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"},
|
||||
{"Sweden", "Suède (la)", "SE", "SWE", "752"},
|
||||
{"Switzerland", "Suisse (la)", "CH", "CHE", "756"},
|
||||
{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"},
|
||||
{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"},
|
||||
{"Thailand", "Thaïlande (la)", "TH", "THA", "764"},
|
||||
{"Togo", "Togo (le)", "TG", "TGO", "768"},
|
||||
{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"},
|
||||
{"Tonga", "Tonga (les)", "TO", "TON", "776"},
|
||||
{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"},
|
||||
{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"},
|
||||
{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"},
|
||||
{"Turkey", "Turquie (la)", "TR", "TUR", "792"},
|
||||
{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"},
|
||||
{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"},
|
||||
{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"},
|
||||
{"Uganda", "Ouganda (l')", "UG", "UGA", "800"},
|
||||
{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"},
|
||||
{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"},
|
||||
{"Egypt", "Égypte (l')", "EG", "EGY", "818"},
|
||||
{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"},
|
||||
{"Guernsey", "Guernesey", "GG", "GGY", "831"},
|
||||
{"Jersey", "Jersey", "JE", "JEY", "832"},
|
||||
{"Isle of Man", "Île de Man", "IM", "IMN", "833"},
|
||||
{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"},
|
||||
{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"},
|
||||
{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"},
|
||||
{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"},
|
||||
{"Uruguay", "Uruguay (l')", "UY", "URY", "858"},
|
||||
{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"},
|
||||
{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"},
|
||||
{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"},
|
||||
{"Samoa", "Samoa (le)", "WS", "WSM", "882"},
|
||||
{"Yemen", "Yémen (le)", "YE", "YEM", "887"},
|
||||
{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"},
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
package govalidator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Contains check if the string contains the substring.
|
||||
func Contains(str, substring string) bool {
|
||||
return strings.Contains(str, substring)
|
||||
}
|
||||
|
||||
// Matches check if string matches the pattern (pattern is regular expression)
|
||||
// In case of error return false
|
||||
func Matches(str, pattern string) bool {
|
||||
match, _ := regexp.MatchString(pattern, str)
|
||||
return match
|
||||
}
|
||||
|
||||
// LeftTrim trim characters from the left-side of the input.
|
||||
// If second argument is empty, it's will be remove leading spaces.
|
||||
func LeftTrim(str, chars string) string {
|
||||
pattern := ""
|
||||
if chars == "" {
|
||||
pattern = "^\\s+"
|
||||
} else {
|
||||
pattern = "^[" + chars + "]+"
|
||||
}
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return string(r.ReplaceAll([]byte(str), []byte("")))
|
||||
}
|
||||
|
||||
// RightTrim trim characters from the right-side of the input.
|
||||
// If second argument is empty, it's will be remove spaces.
|
||||
func RightTrim(str, chars string) string {
|
||||
pattern := ""
|
||||
if chars == "" {
|
||||
pattern = "\\s+$"
|
||||
} else {
|
||||
pattern = "[" + chars + "]+$"
|
||||
}
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return string(r.ReplaceAll([]byte(str), []byte("")))
|
||||
}
|
||||
|
||||
// Trim trim characters from both sides of the input.
|
||||
// If second argument is empty, it's will be remove spaces.
|
||||
func Trim(str, chars string) string {
|
||||
return LeftTrim(RightTrim(str, chars), chars)
|
||||
}
|
||||
|
||||
// WhiteList remove characters that do not appear in the whitelist.
|
||||
func WhiteList(str, chars string) string {
|
||||
pattern := "[^" + chars + "]+"
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return string(r.ReplaceAll([]byte(str), []byte("")))
|
||||
}
|
||||
|
||||
// BlackList remove characters that appear in the blacklist.
|
||||
func BlackList(str, chars string) string {
|
||||
pattern := "[" + chars + "]+"
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return string(r.ReplaceAll([]byte(str), []byte("")))
|
||||
}
|
||||
|
||||
// StripLow remove characters with a numerical value < 32 and 127, mostly control characters.
|
||||
// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
|
||||
func StripLow(str string, keepNewLines bool) string {
|
||||
chars := ""
|
||||
if keepNewLines {
|
||||
chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F"
|
||||
} else {
|
||||
chars = "\x00-\x1F\x7F"
|
||||
}
|
||||
return BlackList(str, chars)
|
||||
}
|
||||
|
||||
// ReplacePattern replace regular expression pattern in string
|
||||
func ReplacePattern(str, pattern, replace string) string {
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return string(r.ReplaceAll([]byte(str), []byte(replace)))
|
||||
}
|
||||
|
||||
// Escape replace <, >, & and " with HTML entities.
|
||||
var Escape = html.EscapeString
|
||||
|
||||
func addSegment(inrune, segment []rune) []rune {
|
||||
if len(segment) == 0 {
|
||||
return inrune
|
||||
}
|
||||
if len(inrune) != 0 {
|
||||
inrune = append(inrune, '_')
|
||||
}
|
||||
inrune = append(inrune, segment...)
|
||||
return inrune
|
||||
}
|
||||
|
||||
// UnderscoreToCamelCase converts from underscore separated form to camel case form.
|
||||
// Ex.: my_func => MyFunc
|
||||
func UnderscoreToCamelCase(s string) string {
|
||||
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
|
||||
}
|
||||
|
||||
// CamelCaseToUnderscore converts from camel case form to underscore separated form.
|
||||
// Ex.: MyFunc => my_func
|
||||
func CamelCaseToUnderscore(str string) string {
|
||||
var output []rune
|
||||
var segment []rune
|
||||
for _, r := range str {
|
||||
if !unicode.IsLower(r) {
|
||||
output = addSegment(output, segment)
|
||||
segment = nil
|
||||
}
|
||||
segment = append(segment, unicode.ToLower(r))
|
||||
}
|
||||
output = addSegment(output, segment)
|
||||
return string(output)
|
||||
}
|
||||
|
||||
// Reverse return reversed string
|
||||
func Reverse(s string) string {
|
||||
r := []rune(s)
|
||||
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// GetLines split string by "\n" and return array of lines
|
||||
func GetLines(s string) []string {
|
||||
return strings.Split(s, "\n")
|
||||
}
|
||||
|
||||
// GetLine return specified line of multiline string
|
||||
func GetLine(s string, index int) (string, error) {
|
||||
lines := GetLines(s)
|
||||
if index < 0 || index >= len(lines) {
|
||||
return "", errors.New("line index out of bounds")
|
||||
}
|
||||
return lines[index], nil
|
||||
}
|
||||
|
||||
// RemoveTags remove all tags from HTML string
|
||||
func RemoveTags(s string) string {
|
||||
return ReplacePattern(s, "<[^>]*>", "")
|
||||
}
|
||||
|
||||
// SafeFileName return safe string that can be used in file names
|
||||
func SafeFileName(str string) string {
|
||||
name := strings.ToLower(str)
|
||||
name = path.Clean(path.Base(name))
|
||||
name = strings.Trim(name, " ")
|
||||
separators, err := regexp.Compile(`[ &_=+:]`)
|
||||
if err == nil {
|
||||
name = separators.ReplaceAllString(name, "-")
|
||||
}
|
||||
legal, err := regexp.Compile(`[^[:alnum:]-.]`)
|
||||
if err == nil {
|
||||
name = legal.ReplaceAllString(name, "")
|
||||
}
|
||||
for strings.Contains(name, "--") {
|
||||
name = strings.Replace(name, "--", "-", -1)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// NormalizeEmail canonicalize an email address.
|
||||
// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
|
||||
// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
|
||||
// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
|
||||
// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
|
||||
// normalized to @gmail.com.
|
||||
func NormalizeEmail(str string) (string, error) {
|
||||
if !IsEmail(str) {
|
||||
return "", fmt.Errorf("%s is not an email", str)
|
||||
}
|
||||
parts := strings.Split(str, "@")
|
||||
parts[0] = strings.ToLower(parts[0])
|
||||
parts[1] = strings.ToLower(parts[1])
|
||||
if parts[1] == "gmail.com" || parts[1] == "googlemail.com" {
|
||||
parts[1] = "gmail.com"
|
||||
parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0]
|
||||
}
|
||||
return strings.Join(parts, "@"), nil
|
||||
}
|
||||
|
||||
// Will truncate a string closest length without breaking words.
|
||||
func Truncate(str string, length int, ending string) string {
|
||||
var aftstr, befstr string
|
||||
if len(str) > length {
|
||||
words := strings.Fields(str)
|
||||
before, present := 0, 0
|
||||
for i := range words {
|
||||
befstr = aftstr
|
||||
before = present
|
||||
aftstr = aftstr + words[i] + " "
|
||||
present = len(aftstr)
|
||||
if present > length && i != 0 {
|
||||
if (length - before) < (present - length) {
|
||||
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
|
||||
} else {
|
||||
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
854
Godeps/_workspace/src/github.com/asaskevich/govalidator/validator.go
generated
vendored
Normal file
854
Godeps/_workspace/src/github.com/asaskevich/govalidator/validator.go
generated
vendored
Normal file
|
@ -0,0 +1,854 @@
|
|||
// Package govalidator is package of validators and sanitizers for strings, structs and collections.
|
||||
package govalidator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var fieldsRequiredByDefault bool
|
||||
|
||||
// SetFieldsRequiredByDefault causes validation to fail when struct fields
|
||||
// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`).
|
||||
// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
|
||||
// type exampleStruct struct {
|
||||
// Name string ``
|
||||
// Email string `valid:"email"`
|
||||
// This, however, will only fail when Email is empty or an invalid email address:
|
||||
// type exampleStruct2 struct {
|
||||
// Name string `valid:"-"`
|
||||
// Email string `valid:"email"`
|
||||
// Lastly, this will only fail when Email is an invalid email address but not when it's empty:
|
||||
// type exampleStruct2 struct {
|
||||
// Name string `valid:"-"`
|
||||
// Email string `valid:"email,optional"`
|
||||
func SetFieldsRequiredByDefault(value bool) {
|
||||
fieldsRequiredByDefault = value
|
||||
}
|
||||
|
||||
// IsEmail check if the string is an email.
|
||||
func IsEmail(str string) bool {
|
||||
// TODO uppercase letters are not supported
|
||||
return rxEmail.MatchString(str)
|
||||
}
|
||||
|
||||
// IsURL check if the string is an URL.
|
||||
func IsURL(str string) bool {
|
||||
if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") {
|
||||
return false
|
||||
}
|
||||
u, err := url.Parse(str)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(u.Host, ".") {
|
||||
return false
|
||||
}
|
||||
if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
|
||||
return false
|
||||
}
|
||||
return rxURL.MatchString(str)
|
||||
|
||||
}
|
||||
|
||||
// IsRequestURL check if the string rawurl, assuming
|
||||
// it was recieved in an HTTP request, is a valid
|
||||
// URL confirm to RFC 3986
|
||||
func IsRequestURL(rawurl string) bool {
|
||||
url, err := url.ParseRequestURI(rawurl)
|
||||
if err != nil {
|
||||
return false //Couldn't even parse the rawurl
|
||||
}
|
||||
if len(url.Scheme) == 0 {
|
||||
return false //No Scheme found
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRequestURI check if the string rawurl, assuming
|
||||
// it was recieved in an HTTP request, is an
|
||||
// absolute URI or an absolute path.
|
||||
func IsRequestURI(rawurl string) bool {
|
||||
_, err := url.ParseRequestURI(rawurl)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsAlpha check if the string contains only letters (a-zA-Z). Empty string is valid.
|
||||
func IsAlpha(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxAlpha.MatchString(str)
|
||||
}
|
||||
|
||||
//IsUTFLetter check if the string contains only unicode letter characters.
|
||||
//Similar to IsAlpha but for all languages. Empty string is valid.
|
||||
func IsUTFLetter(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, c := range str {
|
||||
if !unicode.IsLetter(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// IsAlphanumeric check if the string contains only letters and numbers. Empty string is valid.
|
||||
func IsAlphanumeric(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxAlphanumeric.MatchString(str)
|
||||
}
|
||||
|
||||
// IsUTFLetterNumeric check if the string contains only unicode letters and numbers. Empty string is valid.
|
||||
func IsUTFLetterNumeric(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
for _, c := range str {
|
||||
if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// IsNumeric check if the string contains only numbers. Empty string is valid.
|
||||
func IsNumeric(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxNumeric.MatchString(str)
|
||||
}
|
||||
|
||||
// IsUTFNumeric check if the string contains only unicode numbers of any kind.
|
||||
// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid.
|
||||
func IsUTFNumeric(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
if strings.IndexAny(str, "+-") > 0 {
|
||||
return false
|
||||
}
|
||||
if len(str) > 1 {
|
||||
str = strings.TrimPrefix(str, "-")
|
||||
str = strings.TrimPrefix(str, "+")
|
||||
}
|
||||
for _, c := range str {
|
||||
if unicode.IsNumber(c) == false { //numbers && minus sign are ok
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// IsUTFDigit check if the string contains only unicode radix-10 decimal digits. Empty string is valid.
|
||||
func IsUTFDigit(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
if strings.IndexAny(str, "+-") > 0 {
|
||||
return false
|
||||
}
|
||||
if len(str) > 1 {
|
||||
str = strings.TrimPrefix(str, "-")
|
||||
str = strings.TrimPrefix(str, "+")
|
||||
}
|
||||
for _, c := range str {
|
||||
if !unicode.IsDigit(c) { //digits && minus sign are ok
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// IsHexadecimal check if the string is a hexadecimal number.
|
||||
func IsHexadecimal(str string) bool {
|
||||
return rxHexadecimal.MatchString(str)
|
||||
}
|
||||
|
||||
// IsHexcolor check if the string is a hexadecimal color.
|
||||
func IsHexcolor(str string) bool {
|
||||
return rxHexcolor.MatchString(str)
|
||||
}
|
||||
|
||||
// IsRGBcolor check if the string is a valid RGB color in form rgb(RRR, GGG, BBB).
|
||||
func IsRGBcolor(str string) bool {
|
||||
return rxRGBcolor.MatchString(str)
|
||||
}
|
||||
|
||||
// IsLowerCase check if the string is lowercase. Empty string is valid.
|
||||
func IsLowerCase(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return str == strings.ToLower(str)
|
||||
}
|
||||
|
||||
// IsUpperCase check if the string is uppercase. Empty string is valid.
|
||||
func IsUpperCase(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return str == strings.ToUpper(str)
|
||||
}
|
||||
|
||||
// IsInt check if the string is an integer. Empty string is valid.
|
||||
func IsInt(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxInt.MatchString(str)
|
||||
}
|
||||
|
||||
// IsFloat check if the string is a float.
|
||||
func IsFloat(str string) bool {
|
||||
return str != "" && rxFloat.MatchString(str)
|
||||
}
|
||||
|
||||
// IsDivisibleBy check if the string is a number that's divisible by another.
|
||||
// If second argument is not valid integer or zero, it's return false.
|
||||
// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero).
|
||||
func IsDivisibleBy(str, num string) bool {
|
||||
f, _ := ToFloat(str)
|
||||
p := int64(f)
|
||||
q, _ := ToInt(num)
|
||||
if q == 0 {
|
||||
return false
|
||||
}
|
||||
return (p == 0) || (p%q == 0)
|
||||
}
|
||||
|
||||
// IsNull check if the string is null.
|
||||
func IsNull(str string) bool {
|
||||
return len(str) == 0
|
||||
}
|
||||
|
||||
// IsByteLength check if the string's length (in bytes) falls in a range.
|
||||
func IsByteLength(str string, min, max int) bool {
|
||||
return len(str) >= min && len(str) <= max
|
||||
}
|
||||
|
||||
// IsUUIDv3 check if the string is a UUID version 3.
|
||||
func IsUUIDv3(str string) bool {
|
||||
return rxUUID3.MatchString(str)
|
||||
}
|
||||
|
||||
// IsUUIDv4 check if the string is a UUID version 4.
|
||||
func IsUUIDv4(str string) bool {
|
||||
return rxUUID4.MatchString(str)
|
||||
}
|
||||
|
||||
// IsUUIDv5 check if the string is a UUID version 5.
|
||||
func IsUUIDv5(str string) bool {
|
||||
return rxUUID5.MatchString(str)
|
||||
}
|
||||
|
||||
// IsUUID check if the string is a UUID (version 3, 4 or 5).
|
||||
func IsUUID(str string) bool {
|
||||
return rxUUID.MatchString(str)
|
||||
}
|
||||
|
||||
// IsCreditCard check if the string is a credit card.
|
||||
func IsCreditCard(str string) bool {
|
||||
r, _ := regexp.Compile("[^0-9]+")
|
||||
sanitized := r.ReplaceAll([]byte(str), []byte(""))
|
||||
if !rxCreditCard.MatchString(string(sanitized)) {
|
||||
return false
|
||||
}
|
||||
var sum int64
|
||||
var digit string
|
||||
var tmpNum int64
|
||||
var shouldDouble bool
|
||||
for i := len(sanitized) - 1; i >= 0; i-- {
|
||||
digit = string(sanitized[i:(i + 1)])
|
||||
tmpNum, _ = ToInt(digit)
|
||||
if shouldDouble {
|
||||
tmpNum *= 2
|
||||
if tmpNum >= 10 {
|
||||
sum += ((tmpNum % 10) + 1)
|
||||
} else {
|
||||
sum += tmpNum
|
||||
}
|
||||
} else {
|
||||
sum += tmpNum
|
||||
}
|
||||
shouldDouble = !shouldDouble
|
||||
}
|
||||
|
||||
if sum%10 == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsISBN10 check if the string is an ISBN version 10.
|
||||
func IsISBN10(str string) bool {
|
||||
return IsISBN(str, 10)
|
||||
}
|
||||
|
||||
// IsISBN13 check if the string is an ISBN version 13.
|
||||
func IsISBN13(str string) bool {
|
||||
return IsISBN(str, 13)
|
||||
}
|
||||
|
||||
// IsISBN check if the string is an ISBN (version 10 or 13).
|
||||
// If version value is not equal to 10 or 13, it will be check both variants.
|
||||
func IsISBN(str string, version int) bool {
|
||||
r, _ := regexp.Compile("[\\s-]+")
|
||||
sanitized := r.ReplaceAll([]byte(str), []byte(""))
|
||||
var checksum int32
|
||||
var i int32
|
||||
if version == 10 {
|
||||
if !rxISBN10.MatchString(string(sanitized)) {
|
||||
return false
|
||||
}
|
||||
for i = 0; i < 9; i++ {
|
||||
checksum += (i + 1) * int32(sanitized[i]-'0')
|
||||
}
|
||||
if sanitized[9] == 'X' {
|
||||
checksum += 10 * 10
|
||||
} else {
|
||||
checksum += 10 * int32(sanitized[9]-'0')
|
||||
}
|
||||
if checksum%11 == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else if version == 13 {
|
||||
if !rxISBN13.MatchString(string(sanitized)) {
|
||||
return false
|
||||
}
|
||||
factor := []int32{1, 3}
|
||||
for i = 0; i < 12; i++ {
|
||||
checksum += factor[i%2] * int32(sanitized[i]-'0')
|
||||
}
|
||||
if (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return IsISBN(str, 10) || IsISBN(str, 13)
|
||||
}
|
||||
|
||||
// IsJSON check if the string is valid JSON (note: uses json.Unmarshal).
|
||||
func IsJSON(str string) bool {
|
||||
var js json.RawMessage
|
||||
return json.Unmarshal([]byte(str), &js) == nil
|
||||
}
|
||||
|
||||
// IsMultibyte check if the string contains one or more multibyte chars. Empty string is valid.
|
||||
func IsMultibyte(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxMultibyte.MatchString(str)
|
||||
}
|
||||
|
||||
// IsASCII check if the string contains ASCII chars only. Empty string is valid.
|
||||
func IsASCII(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxASCII.MatchString(str)
|
||||
}
|
||||
|
||||
// IsPrintableASCII check if the string contains printable ASCII chars only. Empty string is valid.
|
||||
func IsPrintableASCII(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxPrintableASCII.MatchString(str)
|
||||
}
|
||||
|
||||
// IsFullWidth check if the string contains any full-width chars. Empty string is valid.
|
||||
func IsFullWidth(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxFullWidth.MatchString(str)
|
||||
}
|
||||
|
||||
// IsHalfWidth check if the string contains any half-width chars. Empty string is valid.
|
||||
func IsHalfWidth(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxHalfWidth.MatchString(str)
|
||||
}
|
||||
|
||||
// IsVariableWidth check if the string contains a mixture of full and half-width chars. Empty string is valid.
|
||||
func IsVariableWidth(str string) bool {
|
||||
if IsNull(str) {
|
||||
return true
|
||||
}
|
||||
return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str)
|
||||
}
|
||||
|
||||
// IsBase64 check if a string is base64 encoded.
|
||||
func IsBase64(str string) bool {
|
||||
return rxBase64.MatchString(str)
|
||||
}
|
||||
|
||||
// IsFilePath check is a string is Win or Unix file path and returns it's type.
|
||||
func IsFilePath(str string) (bool, int) {
|
||||
if rxWinPath.MatchString(str) {
|
||||
//check windows path limit see:
|
||||
// http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
|
||||
if len(str[3:]) > 32767 {
|
||||
return false, Win
|
||||
}
|
||||
return true, Win
|
||||
} else if rxUnixPath.MatchString(str) {
|
||||
return true, Unix
|
||||
}
|
||||
return false, Unknown
|
||||
}
|
||||
|
||||
// IsDataURI checks if a string is base64 encoded data URI such as an image
|
||||
func IsDataURI(str string) bool {
|
||||
dataURI := strings.Split(str, ",")
|
||||
if !rxDataURI.MatchString(dataURI[0]) {
|
||||
return false
|
||||
}
|
||||
return IsBase64(dataURI[1])
|
||||
}
|
||||
|
||||
// IsISO3166Alpha2 checks if a string is valid two-letter country code
|
||||
func IsISO3166Alpha2(str string) bool {
|
||||
for _, entry := range ISO3166List {
|
||||
if str == entry.Alpha2Code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsISO3166Alpha3 checks if a string is valid three-letter country code
|
||||
func IsISO3166Alpha3(str string) bool {
|
||||
for _, entry := range ISO3166List {
|
||||
if str == entry.Alpha3Code {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIP checks if a string is either IP version 4 or 6.
|
||||
func IsIP(str string) bool {
|
||||
return net.ParseIP(str) != nil
|
||||
}
|
||||
|
||||
// IsIPv4 check if the string is an IP version 4.
|
||||
func IsIPv4(str string) bool {
|
||||
ip := net.ParseIP(str)
|
||||
return ip != nil && ip.To4() != nil
|
||||
}
|
||||
|
||||
// IsIPv6 check if the string is an IP version 6.
|
||||
func IsIPv6(str string) bool {
|
||||
ip := net.ParseIP(str)
|
||||
return ip != nil && ip.To4() == nil
|
||||
}
|
||||
|
||||
// IsMAC check if a string is valid MAC address.
|
||||
// Possible MAC formats:
|
||||
// 01:23:45:67:89:ab
|
||||
// 01:23:45:67:89:ab:cd:ef
|
||||
// 01-23-45-67-89-ab
|
||||
// 01-23-45-67-89-ab-cd-ef
|
||||
// 0123.4567.89ab
|
||||
// 0123.4567.89ab.cdef
|
||||
func IsMAC(str string) bool {
|
||||
_, err := net.ParseMAC(str)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsMongoID check if the string is a valid hex-encoded representation of a MongoDB ObjectId.
|
||||
func IsMongoID(str string) bool {
|
||||
return rxHexadecimal.MatchString(str) && (len(str) == 24)
|
||||
}
|
||||
|
||||
// IsLatitude check if a string is valid latitude.
|
||||
func IsLatitude(str string) bool {
|
||||
return rxLatitude.MatchString(str)
|
||||
}
|
||||
|
||||
// IsLongitude check if a string is valid longitude.
|
||||
func IsLongitude(str string) bool {
|
||||
return rxLongitude.MatchString(str)
|
||||
}
|
||||
|
||||
// ValidateStruct use tags for fields
|
||||
func ValidateStruct(s interface{}) (bool, error) {
|
||||
if s == nil {
|
||||
return true, nil
|
||||
}
|
||||
result := true
|
||||
var err error
|
||||
val := reflect.ValueOf(s)
|
||||
if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
// we only accept structs
|
||||
if val.Kind() != reflect.Struct {
|
||||
return false, fmt.Errorf("function only accepts structs; got %s", val.Kind())
|
||||
}
|
||||
var errs Errors
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
valueField := val.Field(i)
|
||||
typeField := val.Type().Field(i)
|
||||
if typeField.PkgPath != "" {
|
||||
continue // Private field
|
||||
}
|
||||
resultField, err := typeCheck(valueField, typeField)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
result = result && resultField
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
err = errs
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
// parseTag splits a struct field's tag into its
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) tagOptions {
|
||||
split := strings.SplitN(tag, ",", -1)
|
||||
return tagOptions(split)
|
||||
}
|
||||
|
||||
func isValidTag(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
switch {
|
||||
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
|
||||
// Backslash and quote chars are reserved, but
|
||||
// otherwise any punctuation chars are allowed
|
||||
// in a tag name.
|
||||
default:
|
||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsSSN will validate the given string as a U.S. Social Security Number
|
||||
func IsSSN(str string) bool {
|
||||
if str == "" || len(str) != 11 {
|
||||
return false
|
||||
}
|
||||
return rxSSN.MatchString(str)
|
||||
}
|
||||
|
||||
// ByteLength check string's length
|
||||
func ByteLength(str string, params ...string) bool {
|
||||
if len(params) == 2 {
|
||||
min, _ := ToInt(params[0])
|
||||
max, _ := ToInt(params[1])
|
||||
return len(str) >= int(min) && len(str) <= int(max)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// StringLength check string's length (including multi byte strings)
|
||||
func StringLength(str string, params ...string) bool {
|
||||
|
||||
if len(params) == 2 {
|
||||
strLength := utf8.RuneCountInString(str)
|
||||
min, _ := ToInt(params[0])
|
||||
max, _ := ToInt(params[1])
|
||||
return strLength >= int(min) && strLength <= int(max)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains returns whether checks that a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (opts tagOptions) contains(optionName string) bool {
|
||||
for i := range opts {
|
||||
tagOpt := opts[i]
|
||||
if tagOpt == optionName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkRequired(v reflect.Value, t reflect.StructField, options tagOptions) (bool, error) {
|
||||
if options.contains("required") {
|
||||
err := fmt.Errorf("non zero value required")
|
||||
return false, Error{t.Name, err}
|
||||
} else if fieldsRequiredByDefault && !options.contains("optional") {
|
||||
err := fmt.Errorf("All fields are required to at least have one validation defined")
|
||||
return false, Error{t.Name, err}
|
||||
}
|
||||
// not required and empty is valid
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func typeCheck(v reflect.Value, t reflect.StructField) (bool, error) {
|
||||
if !v.IsValid() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
tag := t.Tag.Get(tagName)
|
||||
|
||||
// Check if the field should be ignored
|
||||
switch tag {
|
||||
case "":
|
||||
if !fieldsRequiredByDefault {
|
||||
return true, nil
|
||||
}
|
||||
err := fmt.Errorf("All fields are required to at least have one validation defined")
|
||||
return false, Error{t.Name, err}
|
||||
case "-":
|
||||
return true, nil
|
||||
}
|
||||
|
||||
options := parseTag(tag)
|
||||
for i := range options {
|
||||
tagOpt := options[i]
|
||||
if ok := isValidTag(tagOpt); !ok {
|
||||
continue
|
||||
}
|
||||
if validatefunc, ok := CustomTypeTagMap[tagOpt]; ok {
|
||||
options = append(options[:i], options[i+1:]...) // we found our custom validator, so remove it from the options
|
||||
if result := validatefunc(v.Interface()); !result {
|
||||
return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), tagOpt)}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
if isEmptyValue(v) {
|
||||
// an empty value is not validated, check only required
|
||||
return checkRequired(v, t, options)
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Bool,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
|
||||
reflect.Float32, reflect.Float64,
|
||||
reflect.String:
|
||||
// for each tag option check the map of validator functions
|
||||
for i := range options {
|
||||
tagOpt := options[i]
|
||||
negate := false
|
||||
// Check wether the tag looks like '!something' or 'something'
|
||||
if len(tagOpt) > 0 && tagOpt[0] == '!' {
|
||||
tagOpt = string(tagOpt[1:])
|
||||
negate = true
|
||||
}
|
||||
if ok := isValidTag(tagOpt); !ok {
|
||||
err := fmt.Errorf("Unkown Validator %s", tagOpt)
|
||||
return false, Error{t.Name, err}
|
||||
}
|
||||
|
||||
// Check for param validators
|
||||
for key, value := range ParamTagRegexMap {
|
||||
ps := value.FindStringSubmatch(tagOpt)
|
||||
if len(ps) > 0 {
|
||||
if validatefunc, ok := ParamTagMap[key]; ok {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
field := fmt.Sprint(v) // make value into string, then validate with regex
|
||||
if result := validatefunc(field, ps[1:]...); !result && !negate || result && negate {
|
||||
var err error
|
||||
if !negate {
|
||||
err = fmt.Errorf("%s does not validate as %s", field, tagOpt)
|
||||
} else {
|
||||
err = fmt.Errorf("%s does validate as %s", field, tagOpt)
|
||||
}
|
||||
return false, Error{t.Name, err}
|
||||
}
|
||||
default:
|
||||
//Not Yet Supported Types (Fail here!)
|
||||
err := fmt.Errorf("Validator %s doesn't support kind %s", tagOpt, v.Kind())
|
||||
return false, Error{t.Name, err}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if validatefunc, ok := TagMap[tagOpt]; ok {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
field := fmt.Sprint(v) // make value into string, then validate with regex
|
||||
if result := validatefunc(field); !result && !negate || result && negate {
|
||||
var err error
|
||||
if !negate {
|
||||
err = fmt.Errorf("%s does not validate as %s", field, tagOpt)
|
||||
} else {
|
||||
err = fmt.Errorf("%s does validate as %s", field, tagOpt)
|
||||
}
|
||||
return false, Error{t.Name, err}
|
||||
}
|
||||
default:
|
||||
//Not Yet Supported Types (Fail here!)
|
||||
err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", tagOpt, v.Kind(), v)
|
||||
return false, Error{t.Name, err}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
case reflect.Map:
|
||||
if v.Type().Key().Kind() != reflect.String {
|
||||
return false, &UnsupportedTypeError{v.Type()}
|
||||
}
|
||||
var sv stringValues
|
||||
sv = v.MapKeys()
|
||||
sort.Sort(sv)
|
||||
result := true
|
||||
for _, k := range sv {
|
||||
resultItem, err := ValidateStruct(v.MapIndex(k).Interface())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
result = result && resultItem
|
||||
}
|
||||
return result, nil
|
||||
case reflect.Slice:
|
||||
result := true
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
var resultItem bool
|
||||
var err error
|
||||
if v.Index(i).Kind() != reflect.Struct {
|
||||
resultItem, err = typeCheck(v.Index(i), t)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
resultItem, err = ValidateStruct(v.Index(i).Interface())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
result = result && resultItem
|
||||
}
|
||||
return result, nil
|
||||
case reflect.Array:
|
||||
result := true
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
var resultItem bool
|
||||
var err error
|
||||
if v.Index(i).Kind() != reflect.Struct {
|
||||
resultItem, err = typeCheck(v.Index(i), t)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
resultItem, err = ValidateStruct(v.Index(i).Interface())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
result = result && resultItem
|
||||
}
|
||||
return result, nil
|
||||
case reflect.Interface:
|
||||
// If the value is an interface then encode its element
|
||||
if v.IsNil() {
|
||||
return true, nil
|
||||
}
|
||||
return ValidateStruct(v.Interface())
|
||||
case reflect.Ptr:
|
||||
// If the value is a pointer then check its element
|
||||
if v.IsNil() {
|
||||
return true, nil
|
||||
}
|
||||
return typeCheck(v.Elem(), t)
|
||||
case reflect.Struct:
|
||||
return ValidateStruct(v.Interface())
|
||||
default:
|
||||
return false, &UnsupportedTypeError{v.Type()}
|
||||
}
|
||||
}
|
||||
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.String, reflect.Array:
|
||||
return v.Len() == 0
|
||||
case reflect.Map, reflect.Slice:
|
||||
return v.Len() == 0 || v.IsNil()
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
|
||||
}
|
||||
|
||||
// ErrorByField returns error for specified field of the struct
|
||||
// validated by ValidateStruct or empty string if there are no errors
|
||||
// or this field doesn't exists or doesn't have any errors.
|
||||
func ErrorByField(e error, field string) string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return ErrorsByField(e)[field]
|
||||
}
|
||||
|
||||
// ErrorsByField returns map of errors of the struct validated
|
||||
// by ValidateStruct or empty map if there are no errors.
|
||||
func ErrorsByField(e error) map[string]string {
|
||||
m := make(map[string]string)
|
||||
if e == nil {
|
||||
return m
|
||||
}
|
||||
// prototype for ValidateStruct
|
||||
|
||||
switch e.(type) {
|
||||
case Error:
|
||||
m[e.(Error).Name] = e.(Error).Err.Error()
|
||||
case Errors:
|
||||
for _, item := range e.(Errors).Errors() {
|
||||
m[item.(Error).Name] = item.(Error).Err.Error()
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Error returns string equivalent for reflect.Type
|
||||
func (e *UnsupportedTypeError) Error() string {
|
||||
return "validator: unsupported type: " + e.Type.String()
|
||||
}
|
||||
|
||||
func (sv stringValues) Len() int { return len(sv) }
|
||||
func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
|
||||
func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
|
||||
func (sv stringValues) get(i int) string { return sv[i].String() }
|
15
Godeps/_workspace/src/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/asaskevich/govalidator/wercker.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
box: wercker/golang
|
||||
build:
|
||||
steps:
|
||||
- setup-go-workspace
|
||||
|
||||
- script:
|
||||
name: go get
|
||||
code: |
|
||||
go version
|
||||
go get -t ./...
|
||||
|
||||
- script:
|
||||
name: go test
|
||||
code: |
|
||||
go test -race ./...
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
@ -64,7 +63,9 @@ func (b *backend) pathCRLRead(
|
|||
}
|
||||
|
||||
return &logical.Response{
|
||||
Data: structs.New(config).Map(),
|
||||
Data: map[string]interface{}{
|
||||
"expiry": config.Expiry,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@ package pki
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
"github.com/fatih/structs"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
|
@ -43,14 +43,14 @@ for the OCSP servers attribute`,
|
|||
}
|
||||
}
|
||||
|
||||
func validateURLs(urls []string) (string, error) {
|
||||
func validateURLs(urls []string) string {
|
||||
for _, curr := range urls {
|
||||
if _, err := url.Parse(curr); err != nil {
|
||||
return curr, err
|
||||
if !govalidator.IsURL(curr) {
|
||||
return curr
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
return ""
|
||||
}
|
||||
|
||||
func getURLs(req *logical.Request) (*urlEntries, error) {
|
||||
|
@ -121,25 +121,25 @@ func (b *backend) pathWriteURL(
|
|||
if urlsInt, ok := data.GetOk("issuing_certificates"); ok {
|
||||
splitURLs := strings.Split(urlsInt.(string), ",")
|
||||
entries.IssuingCertificates = splitURLs
|
||||
if badUrl, err := validateURLs(entries.CRLDistributionPoints); err != nil {
|
||||
if badUrl := validateURLs(entries.IssuingCertificates); badUrl != "" {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"invalid URL found in issuing certificates; url is %s, error is %s", badUrl, err)), nil
|
||||
"invalid URL found in issuing certificates: %s", badUrl)), nil
|
||||
}
|
||||
}
|
||||
if urlsInt, ok := data.GetOk("crl_distribution_points"); ok {
|
||||
splitURLs := strings.Split(urlsInt.(string), ",")
|
||||
entries.CRLDistributionPoints = splitURLs
|
||||
if badUrl, err := validateURLs(entries.CRLDistributionPoints); err != nil {
|
||||
if badUrl := validateURLs(entries.CRLDistributionPoints); badUrl != "" {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"invalid URL found in CRL distribution points; url is %s, error is %s", badUrl, err)), nil
|
||||
"invalid URL found in CRL distribution points: %s", badUrl)), nil
|
||||
}
|
||||
}
|
||||
if urlsInt, ok := data.GetOk("ocsp_servers"); ok {
|
||||
splitURLs := strings.Split(urlsInt.(string), ",")
|
||||
entries.OCSPServers = splitURLs
|
||||
if badUrl, err := validateURLs(entries.CRLDistributionPoints); err != nil {
|
||||
if badUrl := validateURLs(entries.OCSPServers); badUrl != "" {
|
||||
return logical.ErrorResponse(fmt.Sprintf(
|
||||
"invalid URL found in OCSP servers; url is %s, error is %s", badUrl, err)), nil
|
||||
"invalid URL found in OCSP servers: %s", badUrl)), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"github.com/hashicorp/vault/helper/certutil"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
|
@ -164,14 +163,26 @@ func (b *backend) pathIssueSignCert(
|
|||
}
|
||||
|
||||
resp := b.Secret(SecretCertsType).Response(
|
||||
structs.New(cb).Map(),
|
||||
map[string]interface{}{
|
||||
"certificate": cb.Certificate,
|
||||
"issuing_ca": cb.IssuingCA,
|
||||
"serial_number": cb.SerialNumber,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"serial_number": cb.SerialNumber,
|
||||
})
|
||||
|
||||
if !useCSR {
|
||||
resp.Data["private_key"] = cb.PrivateKey
|
||||
resp.Data["private_key_type"] = cb.PrivateKeyType
|
||||
}
|
||||
|
||||
if format == "der" {
|
||||
resp.Data["certificate"] = base64.StdEncoding.EncodeToString(parsedBundle.CertificateBytes)
|
||||
resp.Data["issuing_ca"] = base64.StdEncoding.EncodeToString(parsedBundle.IssuingCABytes)
|
||||
if !useCSR {
|
||||
resp.Data["private_key"] = base64.StdEncoding.EncodeToString(parsedBundle.PrivateKeyBytes)
|
||||
}
|
||||
}
|
||||
|
||||
resp.Secret.TTL = parsedBundle.Certificate.NotAfter.Sub(time.Now())
|
||||
|
|
|
@ -336,7 +336,7 @@ type roleEntry struct {
|
|||
UseCSRCommonName bool `json:"use_csr_common_name" structs:"use_csr_common_name" mapstructure:"use_csr_common_name"`
|
||||
KeyType string `json:"key_type" structs:"key_type" mapstructure:"key_type"`
|
||||
KeyBits int `json:"key_bits" structs:"key_bits" mapstructure:"key_bits"`
|
||||
MaxPathLength *int `json:"," structs:"," mapstructure:","`
|
||||
MaxPathLength *int `json:",omitempty" structs:",omitempty"`
|
||||
}
|
||||
|
||||
const pathRoleHelpSyn = `
|
||||
|
|
|
@ -99,13 +99,12 @@ is down.
|
|||
|
||||
### You must configure issuing/CRL/OCSP information *in advance*
|
||||
|
||||
**N.B.**: This information is valid for 0.4+.
|
||||
|
||||
This backend serves CRLs from a predictable location, but it is not possible
|
||||
for the backend to know where it is running. Therefore, you must configure
|
||||
desired URLs for the issuing certificate, CRL distribution points, and OCSP
|
||||
servers manually using the `config/urls` endpoint. It is supported to have more
|
||||
than one of each of these.
|
||||
than one of each of these by passing in the multiple URLs as a comma-separated
|
||||
string parameter.
|
||||
|
||||
### No OCSP support, yet
|
||||
|
||||
|
@ -113,20 +112,16 @@ Vault's architecture does not currently allow for a binary protocol such as
|
|||
OCSP to be supported by a backend. As such, you should configure your software
|
||||
to use CRLs for revocation information, with a caching lifetime that feels good
|
||||
to you. Since you are following the advice above about keeping lifetimes short
|
||||
(right?), CRLs should not grow too large.
|
||||
|
||||
If OCSP is important to you, you can configure alternate CRL and/or OCSP
|
||||
servers using `config/urls`.
|
||||
(right?), CRLs should not grow too large, however, you can configure alternate
|
||||
CRL and/or OCSP servers using `config/urls` if you wish.
|
||||
|
||||
If you are using issued certificates for client authentication to Vault, note
|
||||
that as of 0.4, the `cert` authentication endpoint supports being pushed CRLs.
|
||||
that as of 0.4, the `cert` authentication endpoint supports being pushed CRLs,
|
||||
but it cannot read CRLs directly from this backend.
|
||||
|
||||
## Quick Start
|
||||
|
||||
**N.B.**: This quick start assumes that you are pushing a CA certificate and
|
||||
private key in. Since 0.4 there are many different methods of getting CA
|
||||
information into the backend according to your needs, so please note that this
|
||||
is simply one example.
|
||||
#### Mount the backend
|
||||
|
||||
The first step to using the PKI backend is to mount it. Unlike the `generic`
|
||||
backend, the `pki` backend is not mounted by default.
|
||||
|
@ -136,26 +131,98 @@ $ vault mount pki
|
|||
Successfully mounted 'pki' at 'pki'!
|
||||
```
|
||||
|
||||
Next, Vault must be configured with a CA certificate and associated private key. This is done by writing the contents of a file or *stdin*:
|
||||
#### Configure a CA certificate
|
||||
|
||||
Next, Vault must be configured with a CA certificate and associated private
|
||||
key. We'll take advantage of the backend's self-signed root generation support,
|
||||
but Vault also supports generating an intermediate CA (with a CSR for signing)
|
||||
or setting a PEM-encoded certificate and private key bundle directly into the
|
||||
backend.
|
||||
|
||||
Generally you'll want a root certificate to only be used to sign CA
|
||||
intermediate certificates, but for this example we'll proceed as if you will
|
||||
issue certificates directly from the root. As it's a root, we'll want to set a
|
||||
long maximum life time for the certificate; since it honors the maximum mount
|
||||
TTL, first we adjust that:
|
||||
|
||||
```text
|
||||
$ vault write pki/config/ca \
|
||||
pem_bundle="@ca_bundle.pem"
|
||||
Success! Data written to: pki/config/ca
|
||||
$ vault mount-tune -max-lease-ttl=87600h pki
|
||||
Successfully tuned mount 'pki'!
|
||||
```
|
||||
|
||||
or
|
||||
That sets the maximum TTL for secrets issued from the mount to 10 years. (Note
|
||||
that roles can further restrict the maximum TTL.)
|
||||
|
||||
```
|
||||
$ cat bundle.pem | vault write pki/config/ca pem_bundle="-"
|
||||
Success! Data written to: pki/config/ca
|
||||
Now, we generate our root certificate:
|
||||
|
||||
```text
|
||||
$ vault write pki/root/generate/internal common_name=myvault.com ttl=87600h
|
||||
Key Value
|
||||
certificate -----BEGIN CERTIFICATE-----
|
||||
MIIDvTCCAqWgAwIBAgIUAsza+fvOw+Xh9ifYQ0gNN0ruuWcwDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLbXl2YXVsdC5jb20wHhcNMTUxMTE5MTYwNDU5WhcNMjUx
|
||||
MTE2MTYwNDU5WjAWMRQwEgYDVQQDEwtteXZhdWx0LmNvbTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAMUhH4OLf/sa6GuJONGC/CWLY7nDbfH8jAaCKgqV
|
||||
eJ81KrmcgP8WPhoFsYHFQEQXQZcrJagwYfm19jYn3CaqrYPbciv9bcWi+ECxZV3x
|
||||
Hs/YdCFk7KgDGCci37w+cy6fSB943FKJqqVbvPv0odmq6LvgGGgneznvuvkIrOWG
|
||||
qVDrDdvbEZ01XAyzUQJaaiJXExN+6xm1HcBoypCP8ZjjnXHcFQvw2QBItLRU7iUd
|
||||
ESFgbrkrSPW3HA6KF0ov2qFMoHTiQ6aM4KaHPmXcFPicugYR9owZfZ4lwWJCqT7j
|
||||
EkhokaMgHnvyRScuiRZhQm8ppHZoYsqrc3glfEuxGHkS+0cCAwEAAaOCAQEwgf4w
|
||||
DgYDVR0PAQH/BAQDAgGuMBMGA1UdJQQMMAoGCCsGAQUFBwMJMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLvAbt0eUUOoo7hjKiQM2bRqDKrZMB8GA1UdIwQYMBaA
|
||||
FLvAbt0eUUOoo7hjKiQM2bRqDKrZMDsGCCsGAQUFBwEBBC8wLTArBggrBgEFBQcw
|
||||
AoYfaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jYTAWBgNVHREEDzANggtt
|
||||
eXZhdWx0LmNvbTAxBgNVHR8EKjAoMCagJKAihiBodHRwOi8vMTI3LjAuMC4xOjgy
|
||||
MDAvdjEvcGtpL2NybDANBgkqhkiG9w0BAQsFAAOCAQEAVSgIRl6XJs95D7iXGzeQ
|
||||
Ab8OIei779k0pD7xxS/+knY3TM6733zL/LXs4BEL3wfcQWoDrMtCW0Ook455sAOE
|
||||
PSnTaZYQSH/F74VawWhSee4ZyiWq+sTUI4IzqYG3IS36mCyb0t6RxEb3aoQ87WHs
|
||||
BHIB6uWbj6WoGHYM8ESxY89aY9jnX3xSs1HuluVW1uPrpIoa/eudpyV40Y1+9RNM
|
||||
6fCX5LHGM7vKYxqvudYe+7G1MdKVBQg17h6XuieiUswVt2/HvDlNr+9DHrUla9Ve
|
||||
Ig43v+grirlG7DrAr6Aiu/MVWKJP6CvNwG/XzrGaqd6KqSsE+8oIGR9tCTuPxI6v
|
||||
SQ==
|
||||
-----END CERTIFICATE-----
|
||||
expiration 1.763309099e+09
|
||||
issuing_ca -----BEGIN CERTIFICATE-----
|
||||
MIIDvTCCAqWgAwIBAgIUAsza+fvOw+Xh9ifYQ0gNN0ruuWcwDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLbXl2YXVsdC5jb20wHhcNMTUxMTE5MTYwNDU5WhcNMjUx
|
||||
MTE2MTYwNDU5WjAWMRQwEgYDVQQDEwtteXZhdWx0LmNvbTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAMUhH4OLf/sa6GuJONGC/CWLY7nDbfH8jAaCKgqV
|
||||
eJ81KrmcgP8WPhoFsYHFQEQXQZcrJagwYfm19jYn3CaqrYPbciv9bcWi+ECxZV3x
|
||||
Hs/YdCFk7KgDGCci37w+cy6fSB943FKJqqVbvPv0odmq6LvgGGgneznvuvkIrOWG
|
||||
qVDrDdvbEZ01XAyzUQJaaiJXExN+6xm1HcBoypCP8ZjjnXHcFQvw2QBItLRU7iUd
|
||||
ESFgbrkrSPW3HA6KF0ov2qFMoHTiQ6aM4KaHPmXcFPicugYR9owZfZ4lwWJCqT7j
|
||||
EkhokaMgHnvyRScuiRZhQm8ppHZoYsqrc3glfEuxGHkS+0cCAwEAAaOCAQEwgf4w
|
||||
DgYDVR0PAQH/BAQDAgGuMBMGA1UdJQQMMAoGCCsGAQUFBwMJMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLvAbt0eUUOoo7hjKiQM2bRqDKrZMB8GA1UdIwQYMBaA
|
||||
FLvAbt0eUUOoo7hjKiQM2bRqDKrZMDsGCCsGAQUFBwEBBC8wLTArBggrBgEFBQcw
|
||||
AoYfaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jYTAWBgNVHREEDzANggtt
|
||||
eXZhdWx0LmNvbTAxBgNVHR8EKjAoMCagJKAihiBodHRwOi8vMTI3LjAuMC4xOjgy
|
||||
MDAvdjEvcGtpL2NybDANBgkqhkiG9w0BAQsFAAOCAQEAVSgIRl6XJs95D7iXGzeQ
|
||||
Ab8OIei779k0pD7xxS/+knY3TM6733zL/LXs4BEL3wfcQWoDrMtCW0Ook455sAOE
|
||||
PSnTaZYQSH/F74VawWhSee4ZyiWq+sTUI4IzqYG3IS36mCyb0t6RxEb3aoQ87WHs
|
||||
BHIB6uWbj6WoGHYM8ESxY89aY9jnX3xSs1HuluVW1uPrpIoa/eudpyV40Y1+9RNM
|
||||
6fCX5LHGM7vKYxqvudYe+7G1MdKVBQg17h6XuieiUswVt2/HvDlNr+9DHrUla9Ve
|
||||
Ig43v+grirlG7DrAr6Aiu/MVWKJP6CvNwG/XzrGaqd6KqSsE+8oIGR9tCTuPxI6v
|
||||
SQ==
|
||||
-----END CERTIFICATE-----
|
||||
serial_number 02:cc:da:f9:fb:ce:c3:e5:e1:f6:27:d8:43:48:0d:37:4a:ee:b9:67
|
||||
```
|
||||
|
||||
Although in this example the value being piped into *stdin* could be passed
|
||||
directly into the Vault CLI command, a more complex usage might be to use
|
||||
[Ansible](http://www.ansible.com) to securely store the certificate and private
|
||||
key in an `ansible-vault` file, then have an `ansible-playbook` command decrypt
|
||||
this value and pass it in to Vault.
|
||||
The returned certificate is purely informational; it and its private key are
|
||||
safely stored in the backend mount.
|
||||
|
||||
#### Set URL configuration
|
||||
|
||||
Generated certificates can have the CRL location and the location of the
|
||||
issuing certificate encoded. These values must be set manually, but can be
|
||||
changed at any time.
|
||||
|
||||
```text
|
||||
$ vault write pki/config/urls issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"
|
||||
Success! Data written to: pki/ca/urls
|
||||
```
|
||||
|
||||
#### Configure a role
|
||||
|
||||
The next step is to configure a role. A role is a logical name that maps to a
|
||||
policy used to generated those credentials. For example, let's create an
|
||||
|
@ -168,6 +235,8 @@ $ vault write pki/roles/example-dot-com \
|
|||
Success! Data written to: pki/roles/example-dot-com
|
||||
```
|
||||
|
||||
#### Generate credentials
|
||||
|
||||
By writing to the `roles/example-dot-com` path we are defining the
|
||||
`example-dot-com` role. To generate a new set of credentials, we simply write
|
||||
to the `issue` endpoint with that role name: Vault is now configured to create
|
||||
|
@ -176,30 +245,86 @@ and manage certificates!
|
|||
```text
|
||||
$ vault write pki/issue/example-dot-com \
|
||||
common_name=blah.example.com
|
||||
Key Value
|
||||
lease_id pki/issue/example-dot-com/819393b5-e1a1-9efd-b72f-4dc3a1972e31
|
||||
lease_duration 259200
|
||||
lease_renewable false
|
||||
certificate -----BEGIN CERTIFICATE-----
|
||||
MIIECDCCAvKgAwIBAgIUXmLrLkTdBIOOIYg2/BXO7docKfUwCwYJKoZIhvcNAQEL
|
||||
...
|
||||
az3gfwlOqVTdgi/ZVAtIzhSEJ0OY136bq4NOaw==
|
||||
Key Value
|
||||
lease_id pki/issue/example-dot-com/32db49a9-61dd-f9ca-f4a6-aaefafe53739
|
||||
lease_duration 259199
|
||||
lease_renewable false
|
||||
EmbeddedPrivateKey map[private_key:-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA760wGT6yzYx85rJZcfGSK59BN2G2GgJGs62VBdXGRFav5Ag1
|
||||
/nhMqq9Rs/TVm6pNF5iy4zRH+8PI63jQ1jbsMv5+IacJtg+M90GbSC4tGAT2HWdv
|
||||
8nx+2WfU0lsFm04wiy7xvDMixQmTSNwO+BrqkGZbf/1b6QealQxMih2XIZ/b+Pas
|
||||
dw9+Bps9i8DsqVkSuuQ+oiCU8mr/qb9LkxgSI2GzN8nD9sCYx8bGPX0ML0zrUX0P
|
||||
AwHo7ZXFRrV3ZGIrrPPrjPSWZd8hSQuBhkLWKAz9tcyighI4AH93IE7tCLCFljzB
|
||||
KT4ZTId9Izh+UHiCsZgnHTf8Oc8u9eWJvMnl9QIDAQABAoIBACdHBn7cm4i74Swd
|
||||
RjM6F242pwVgaetRPCzt4WIXamqAmkqQXejZ5haCrK0FDox4GDIpwAcj5jgIGcEd
|
||||
B3xTe9nPgvQY36qKWdubUMs/Zfxts7eV3+6pIFCYh3QBnqWY5rOoec0RxDzuGMjz
|
||||
5zAZmR8Y5x3Oc2V47YhHf7OSrdXAf6IDd4oDvzIO6JoYnTAE6VNBTRSrViyTmJ5c
|
||||
GTxQQZCroFKY1sg1rxRGEEyl4uRje5CbejEmPugEU6bawRYctNmFt8WZhKhbRx1j
|
||||
6L8HFvU84ldWgdBUxwk+rAdGHKuAGvObyt6OEu3rDIRGWIyM/c+qfQIARqIcbu2x
|
||||
JVrRnAECgYEA/cotIg6K/s/v+0zuf+VLcqGPjuRnVYH3IRMbaHxb2z23ojqwiDyF
|
||||
NO5rIIw8nswvmemECwchGwNd2QyiamNSjczqDgzlZQ4WnqPYvMRe6LqjPRNKevwu
|
||||
3bH5RK1Whs2347NtE2Hsr2yBjldPCpiBVMDRNWt5wNXwfvel/yMOL8ECgYEA8cOL
|
||||
0NUhyVk6l++oNE6J7C4NpZZuhnkWNnMPQS8bnAkapQF06/6VOLTovlJtWHPWqzfx
|
||||
IW4vjr4wBF5X5BSow8TmRVi/JRgYOx75/NhlQzrJ9xI7YZ2RLk4Bu7LlPIbAan9l
|
||||
mL8w9ggdtfzBY0AUrhFgt//MtZCb04GJK4r6wzUCgYEA7ByomMLG9gIm1rngOgTt
|
||||
iJxenyZILPlZHeBU44SvQO3OZpQLoPQNSg12hTVzQJnev2bNfiUH28XqDgD/KdN1
|
||||
/o7iQmglEztkN/tfrV9UnVjmfe/wnzL3tzHZItfRZGEH8EQB+sJkHWDpt6/qzYTM
|
||||
7jjlarmu8IUZ1kY4nYHTtQECgYB7JDQCAZ089oL+wqY1Rk/ACdEPp6jSO7jRsRdz
|
||||
BzCT8JsusqhAUCLniFYIIunpJM+R1GOwuHPmy/6fKwKvfMpdNNxpNAPgO/6TlnqU
|
||||
jBBAByC6BQJT/TLml/sQBNX4V1aIqC0yeSfSGV4wxPYnvIsMAnP2JIe7b1W/jHo2
|
||||
wIprhQKBgQCRj2D/ddj13lRuHSxDMcrk9P0IQvSK20LO5IMJ6edRxMXsKjuCQ0WR
|
||||
c4mibLD0DYNoT3fTad9at3QYD0GMuIfzPRpMAd1sIHIQRe/wjQY2zIpAC4yjFW8U
|
||||
Y/eFKpuvJIT6CPU1lNBZQFc1Td0bKpdTVlJyhOHQMqLrinuSXyWwaw==
|
||||
-----END RSA PRIVATE KEY----- private_key_type:rsa]
|
||||
certificate -----BEGIN CERTIFICATE-----
|
||||
MIIDvzCCAqegAwIBAgIUE5fVBU0TBf6QJ7Z+Vc+0x865S1AwDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLbXl2YXVsdC5jb20wHhcNMTUxMTE5MTYxNTMyWhcNMTUx
|
||||
MTIyMTYxNTMyWjAbMRkwFwYDVQQDExBibGFoLmV4YW1wbGUuY29tMIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA760wGT6yzYx85rJZcfGSK59BN2G2GgJG
|
||||
s62VBdXGRFav5Ag1/nhMqq9Rs/TVm6pNF5iy4zRH+8PI63jQ1jbsMv5+IacJtg+M
|
||||
90GbSC4tGAT2HWdv8nx+2WfU0lsFm04wiy7xvDMixQmTSNwO+BrqkGZbf/1b6Qea
|
||||
lQxMih2XIZ/b+Pasdw9+Bps9i8DsqVkSuuQ+oiCU8mr/qb9LkxgSI2GzN8nD9sCY
|
||||
x8bGPX0ML0zrUX0PAwHo7ZXFRrV3ZGIrrPPrjPSWZd8hSQuBhkLWKAz9tcyighI4
|
||||
AH93IE7tCLCFljzBKT4ZTId9Izh+UHiCsZgnHTf8Oc8u9eWJvMnl9QIDAQABo4H/
|
||||
MIH8MA4GA1UdDwEB/wQEAwIDqDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH
|
||||
AwIwHQYDVR0OBBYEFNz/tm08rZmd2QPuBIsuCqdgozSjMB8GA1UdIwQYMBaAFLvA
|
||||
bt0eUUOoo7hjKiQM2bRqDKrZMDsGCCsGAQUFBwEBBC8wLTArBggrBgEFBQcwAoYf
|
||||
aHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jYTAbBgNVHREEFDASghBibGFo
|
||||
LmV4YW1wbGUuY29tMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly8xMjcuMC4wLjE6
|
||||
ODIwMC92MS9wa2kvY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQAC+Wbov3HfdZrvrG5i
|
||||
poODurUkcluxGBXNUjAcXR4IOne14eXszlo7Z3mJY32V4pWliQhYJTuknCC2c6l1
|
||||
Qgbr+Rw8iYqrap09osLws1vj1vAp6qwcNhjNlZveFBsBtUcgbF6t8JF2P8A11CR6
|
||||
G/y5jUSXUWTuXf+dD63DSzOlk8+F7ocduXsOLIBTSqQUQhn68zG4JQb020wbbCFI
|
||||
xHZgEdNedrqo96zhMVnoHbVqm+SnNv6QLHj/2HtSTHA6oICpVcXLtWk+WojViIpl
|
||||
eeMEKTzLWK53Ijv3bCGJD8YNc/RpzEAKaVUvYgQdSZvzDOfRs2rugYeq/vWH7h18
|
||||
rySo
|
||||
-----END CERTIFICATE-----
|
||||
issuing_ca -----BEGIN CERTIFICATE-----
|
||||
MIIDUTCCAjmgAwIBAgIJAKM+z4MSfw2mMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
|
||||
...
|
||||
issuing_ca -----BEGIN CERTIFICATE-----
|
||||
MIIDvTCCAqWgAwIBAgIUAsza+fvOw+Xh9ifYQ0gNN0ruuWcwDQYJKoZIhvcNAQEL
|
||||
BQAwFjEUMBIGA1UEAxMLbXl2YXVsdC5jb20wHhcNMTUxMTE5MTYwNDU5WhcNMjUx
|
||||
MTE2MTYwNDU5WjAWMRQwEgYDVQQDEwtteXZhdWx0LmNvbTCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAMUhH4OLf/sa6GuJONGC/CWLY7nDbfH8jAaCKgqV
|
||||
eJ81KrmcgP8WPhoFsYHFQEQXQZcrJagwYfm19jYn3CaqrYPbciv9bcWi+ECxZV3x
|
||||
Hs/YdCFk7KgDGCci37w+cy6fSB943FKJqqVbvPv0odmq6LvgGGgneznvuvkIrOWG
|
||||
qVDrDdvbEZ01XAyzUQJaaiJXExN+6xm1HcBoypCP8ZjjnXHcFQvw2QBItLRU7iUd
|
||||
ESFgbrkrSPW3HA6KF0ov2qFMoHTiQ6aM4KaHPmXcFPicugYR9owZfZ4lwWJCqT7j
|
||||
EkhokaMgHnvyRScuiRZhQm8ppHZoYsqrc3glfEuxGHkS+0cCAwEAAaOCAQEwgf4w
|
||||
DgYDVR0PAQH/BAQDAgGuMBMGA1UdJQQMMAoGCCsGAQUFBwMJMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLvAbt0eUUOoo7hjKiQM2bRqDKrZMB8GA1UdIwQYMBaA
|
||||
FLvAbt0eUUOoo7hjKiQM2bRqDKrZMDsGCCsGAQUFBwEBBC8wLTArBggrBgEFBQcw
|
||||
AoYfaHR0cDovLzEyNy4wLjAuMTo4MjAwL3YxL3BraS9jYTAWBgNVHREEDzANggtt
|
||||
eXZhdWx0LmNvbTAxBgNVHR8EKjAoMCagJKAihiBodHRwOi8vMTI3LjAuMC4xOjgy
|
||||
MDAvdjEvcGtpL2NybDANBgkqhkiG9w0BAQsFAAOCAQEAVSgIRl6XJs95D7iXGzeQ
|
||||
Ab8OIei779k0pD7xxS/+knY3TM6733zL/LXs4BEL3wfcQWoDrMtCW0Ook455sAOE
|
||||
PSnTaZYQSH/F74VawWhSee4ZyiWq+sTUI4IzqYG3IS36mCyb0t6RxEb3aoQ87WHs
|
||||
BHIB6uWbj6WoGHYM8ESxY89aY9jnX3xSs1HuluVW1uPrpIoa/eudpyV40Y1+9RNM
|
||||
6fCX5LHGM7vKYxqvudYe+7G1MdKVBQg17h6XuieiUswVt2/HvDlNr+9DHrUla9Ve
|
||||
Ig43v+grirlG7DrAr6Aiu/MVWKJP6CvNwG/XzrGaqd6KqSsE+8oIGR9tCTuPxI6v
|
||||
SQ==
|
||||
-----END CERTIFICATE-----
|
||||
private_key -----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA0cczc7Y2yIu7aD/IaDi23Io+tvvDS9XaXXDUFW1kqd58P83r
|
||||
...
|
||||
3xhCNnZ3CMQaM2I48sloVK/XoikMLb5MZwOUQn/V+TrhWP4Lu7qD
|
||||
-----END RSA PRIVATE KEY-----
|
||||
serial_number 5e:62:eb:2e:44:dd:04:83:8e:21:88:36:fc:15:ce:ed:da:1c:29:f5
|
||||
serial_number 13:97:d5:05:4d:13:05:fe:90:27:b6:7e:55:cf:b4:c7:ce:b9:4b:50
|
||||
```
|
||||
|
||||
Note that this is a write, not a read, to allow values to be passed in at
|
||||
request time.
|
||||
|
||||
Vault has now generated a new set of credentials using the `example-dot-com`
|
||||
role configuration. Here we see the dynamically generated private key and
|
||||
certificate. The issuing CA certificate is returned as well.
|
||||
|
@ -416,6 +541,7 @@ subpath for interactive help output.
|
|||
</dl>
|
||||
|
||||
### /pki/config/urls
|
||||
|
||||
#### GET
|
||||
|
||||
<dl class="api">
|
||||
|
@ -425,7 +551,7 @@ subpath for interactive help output.
|
|||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>POST</dd>
|
||||
<dd>GET</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/pki/config/urls`</dd>
|
||||
|
@ -1035,7 +1161,10 @@ subpath for interactive help output.
|
|||
overwrite any previously-existing private key and certificate._ If the path
|
||||
ends with `exported`, the private key will be returned in the response; if
|
||||
it is `internal` the private key will not be returned and *cannot be
|
||||
retrieved later*. Distribution points use the values set via `config/urls`.
|
||||
retrieved later*. Distribution points use the values set via
|
||||
`config/urls`. <br /><br />Vault does _not_ revoke this certificate (since
|
||||
it could not sign the CRL with an expired certificate), however, this
|
||||
endpoint does honor the maximum mount TTL.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
|
|
Loading…
Reference in New Issue