mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
0a970aef8a
This moves the OpenID Connect storage from memory into the SQL storage, making it persistent and allowing it to be used with clustered deployments like the rest of Authelia.
293 lines
7.7 KiB
Go
293 lines
7.7 KiB
Go
package utils
|
|
|
|
import (
|
|
crand "crypto/rand"
|
|
"fmt"
|
|
"math/rand"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
"unicode"
|
|
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
// IsStringAbsURL checks a string can be parsed as a URL and that is IsAbs and if it can't it returns an error
|
|
// describing why.
|
|
func IsStringAbsURL(input string) (err error) {
|
|
parsedURL, err := url.Parse(input)
|
|
if err != nil {
|
|
return fmt.Errorf("could not parse '%s' as a URL", input)
|
|
}
|
|
|
|
if !parsedURL.IsAbs() {
|
|
return fmt.Errorf("the url '%s' is not absolute because it doesn't start with a scheme like 'http://' or 'https://'", input)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsStringAlphaNumeric returns false if any rune in the string is not alpha-numeric.
|
|
func IsStringAlphaNumeric(input string) bool {
|
|
for _, r := range input {
|
|
if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// IsStringInSlice checks if a single string is in a slice of strings.
|
|
func IsStringInSlice(needle string, haystack []string) (inSlice bool) {
|
|
for _, b := range haystack {
|
|
if b == needle {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsStringInSliceSuffix checks if the needle string has one of the suffixes in the haystack.
|
|
func IsStringInSliceSuffix(needle string, haystack []string) (hasSuffix bool) {
|
|
for _, straw := range haystack {
|
|
if strings.HasSuffix(needle, straw) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsStringInSliceFold checks if a single string is in a slice of strings but uses strings.EqualFold to compare them.
|
|
func IsStringInSliceFold(needle string, haystack []string) (inSlice bool) {
|
|
for _, b := range haystack {
|
|
if strings.EqualFold(b, needle) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsStringInSliceContains checks if a single string is in an array of strings.
|
|
func IsStringInSliceContains(needle string, haystack []string) (inSlice bool) {
|
|
for _, b := range haystack {
|
|
if strings.Contains(needle, b) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsStringSliceContainsAll checks if the haystack contains all strings in the needles.
|
|
func IsStringSliceContainsAll(needles []string, haystack []string) (inSlice bool) {
|
|
for _, n := range needles {
|
|
if !IsStringInSlice(n, haystack) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// IsStringSliceContainsAny checks if the haystack contains any of the strings in the needles.
|
|
func IsStringSliceContainsAny(needles []string, haystack []string) (inSlice bool) {
|
|
for _, n := range needles {
|
|
if IsStringInSlice(n, haystack) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// SliceString splits a string s into an array with each item being a max of int d
|
|
// d = denominator, n = numerator, q = quotient, r = remainder.
|
|
func SliceString(s string, d int) (array []string) {
|
|
n := len(s)
|
|
q := n / d
|
|
r := n % d
|
|
|
|
for i := 0; i < q; i++ {
|
|
array = append(array, s[i*d:i*d+d])
|
|
if i+1 == q && r != 0 {
|
|
array = append(array, s[i*d+d:])
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func isStringSlicesDifferent(a, b []string, method func(s string, b []string) bool) (different bool) {
|
|
if len(a) != len(b) {
|
|
return true
|
|
}
|
|
|
|
for _, s := range a {
|
|
if !method(s, b) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsStringSlicesDifferent checks two slices of strings and on the first occurrence of a string item not existing in the
|
|
// other slice returns true, otherwise returns false.
|
|
func IsStringSlicesDifferent(a, b []string) (different bool) {
|
|
return isStringSlicesDifferent(a, b, IsStringInSlice)
|
|
}
|
|
|
|
// IsStringSlicesDifferentFold checks two slices of strings and on the first occurrence of a string item not existing in
|
|
// the other slice (case insensitive) returns true, otherwise returns false.
|
|
func IsStringSlicesDifferentFold(a, b []string) (different bool) {
|
|
return isStringSlicesDifferent(a, b, IsStringInSliceFold)
|
|
}
|
|
|
|
// IsURLInSlice returns true if the needle url.URL is in the []url.URL haystack.
|
|
func IsURLInSlice(needle url.URL, haystack []url.URL) (has bool) {
|
|
for i := 0; i < len(haystack); i++ {
|
|
if strings.EqualFold(needle.String(), haystack[i].String()) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// StringSliceFromURLs returns a []string from a []url.URL.
|
|
func StringSliceFromURLs(urls []url.URL) []string {
|
|
result := make([]string, len(urls))
|
|
|
|
for i := 0; i < len(urls); i++ {
|
|
result[i] = urls[i].String()
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// URLsFromStringSlice returns a []url.URL from a []string.
|
|
func URLsFromStringSlice(urls []string) []url.URL {
|
|
var result []url.URL
|
|
|
|
for i := 0; i < len(urls); i++ {
|
|
u, err := url.Parse(urls[i])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
result = append(result, *u)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// OriginFromURL returns an origin url.URL given another url.URL.
|
|
func OriginFromURL(u url.URL) (origin url.URL) {
|
|
return url.URL{
|
|
Scheme: u.Scheme,
|
|
Host: u.Host,
|
|
}
|
|
}
|
|
|
|
// StringSlicesDelta takes a before and after []string and compares them returning a added and removed []string.
|
|
func StringSlicesDelta(before, after []string) (added, removed []string) {
|
|
for _, s := range before {
|
|
if !IsStringInSlice(s, after) {
|
|
removed = append(removed, s)
|
|
}
|
|
}
|
|
|
|
for _, s := range after {
|
|
if !IsStringInSlice(s, before) {
|
|
added = append(added, s)
|
|
}
|
|
}
|
|
|
|
return added, removed
|
|
}
|
|
|
|
// RandomString returns a random string with a given length with values from the provided characters. When crypto is set
|
|
// to false we use math/rand and when it's set to true we use crypto/rand. The crypto option should always be set to true
|
|
// excluding when the task is time sensitive and would not benefit from extra randomness.
|
|
func RandomString(n int, characters string, crypto bool) (randomString string) {
|
|
return string(RandomBytes(n, characters, crypto))
|
|
}
|
|
|
|
// RandomBytes returns a random []byte with a given length with values from the provided characters. When crypto is set
|
|
// to false we use math/rand and when it's set to true we use crypto/rand. The crypto option should always be set to true
|
|
// excluding when the task is time sensitive and would not benefit from extra randomness.
|
|
func RandomBytes(n int, characters string, crypto bool) (bytes []byte) {
|
|
bytes = make([]byte, n)
|
|
|
|
if crypto {
|
|
_, _ = crand.Read(bytes)
|
|
} else {
|
|
_, _ = rand.Read(bytes) //nolint:gosec // As this is an option when using this function it's not necessary to be concerned about this.
|
|
}
|
|
|
|
for i, b := range bytes {
|
|
bytes[i] = characters[b%byte(len(characters))]
|
|
}
|
|
|
|
return bytes
|
|
}
|
|
|
|
// StringHTMLEscape escapes chars for a HTML body.
|
|
func StringHTMLEscape(input string) (output string) {
|
|
return htmlEscaper.Replace(input)
|
|
}
|
|
|
|
// StringJoinDelimitedEscaped joins a string with a specified rune delimiter after escaping any instance of that string
|
|
// in the string slice. Used with StringSplitDelimitedEscaped.
|
|
func StringJoinDelimitedEscaped(value []string, delimiter rune) string {
|
|
escaped := make([]string, len(value))
|
|
for k, v := range value {
|
|
escaped[k] = strings.ReplaceAll(v, string(delimiter), "\\"+string(delimiter))
|
|
}
|
|
|
|
return strings.Join(escaped, string(delimiter))
|
|
}
|
|
|
|
// StringSplitDelimitedEscaped splits a string with a specified rune delimiter after unescaping any instance of that
|
|
// string in the string slice that has been escaped. Used with StringJoinDelimitedEscaped.
|
|
func StringSplitDelimitedEscaped(value string, delimiter rune) (out []string) {
|
|
var escape bool
|
|
|
|
split := strings.FieldsFunc(value, func(r rune) bool {
|
|
if r == '\\' {
|
|
escape = !escape
|
|
} else if escape && r != delimiter {
|
|
escape = false
|
|
}
|
|
|
|
return !escape && r == delimiter
|
|
})
|
|
|
|
for k, v := range split {
|
|
split[k] = strings.ReplaceAll(v, "\\"+string(delimiter), string(delimiter))
|
|
}
|
|
|
|
return split
|
|
}
|
|
|
|
// JoinAndCanonicalizeHeaders join header strings by a given sep.
|
|
func JoinAndCanonicalizeHeaders(sep []byte, headers ...string) (joined []byte) {
|
|
for i, header := range headers {
|
|
if i != 0 {
|
|
joined = append(joined, sep...)
|
|
}
|
|
|
|
joined = fasthttp.AppendNormalizedHeaderKey(joined, header)
|
|
}
|
|
|
|
return joined
|
|
}
|
|
|
|
func init() {
|
|
rand.Seed(time.Now().UnixNano())
|
|
}
|