authelia/internal/utils/strings.go
James Elliott ef549f851d
feat(oidc): add additional config options, accurate token times, and refactoring (#1991)
* This gives admins more control over their OIDC installation exposing options that had defaults before. Things like lifespans for authorize codes, access tokens, id tokens, refresh tokens, a option to enable the debug client messages, minimum parameter entropy. It also allows admins to configure the response modes.
* Additionally this records specific values about a users session indicating when they performed a specific authz factor so this is represented in the token accurately. 
* Lastly we also implemented a OIDC key manager which calculates the kid for jwk's using the SHA1 digest instead of being static, or more specifically the first 7 chars. As per https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key#section-8.1.1 the kid should not exceed 8 chars. While it's allowed to exceed 8 chars, it must only be done so with a compelling reason, which we do not have.
2021-07-04 09:44:30 +10:00

142 lines
3.5 KiB
Go

package utils
import (
"fmt"
"math/rand"
"net/url"
"strings"
"time"
"unicode"
)
// 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
}
// 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
}
// 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)
}
// 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 generate a random string of n characters.
func RandomString(n int, characters []rune) (randomString string) {
rand.Seed(time.Now().UnixNano())
b := make([]rune, n)
for i := range b {
b[i] = characters[rand.Intn(len(characters))] //nolint:gosec // Likely isn't necessary to use the more expensive crypto/rand for this utility func.
}
return string(b)
}