mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-11-05 16:45:44 +00:00
347 lines
9.7 KiB
Go
347 lines
9.7 KiB
Go
package utils_test
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
"tinyauth/internal/config"
|
|
"tinyauth/internal/utils"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gotest.tools/v3/assert"
|
|
)
|
|
|
|
func TestGetRootDomain(t *testing.T) {
|
|
// Normal case
|
|
domain := "http://sub.tinyauth.app"
|
|
expected := "tinyauth.app"
|
|
result, err := utils.GetCookieDomain(domain)
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, expected, result)
|
|
|
|
// Domain with multiple subdomains
|
|
domain = "http://b.c.tinyauth.app"
|
|
expected = "c.tinyauth.app"
|
|
result, err = utils.GetCookieDomain(domain)
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, expected, result)
|
|
|
|
// Domain with no subdomain
|
|
domain = "http://tinyauth.app"
|
|
expected = "tinyauth.app"
|
|
_, err = utils.GetCookieDomain(domain)
|
|
assert.Error(t, err, "invalid app url, must be at least second level domain")
|
|
|
|
// Invalid domain (only TLD)
|
|
domain = "com"
|
|
_, err = utils.GetCookieDomain(domain)
|
|
assert.ErrorContains(t, err, "invalid app url, must be at least second level domain")
|
|
|
|
// IP address
|
|
domain = "http://10.10.10.10"
|
|
_, err = utils.GetCookieDomain(domain)
|
|
assert.ErrorContains(t, err, "IP addresses not allowed")
|
|
|
|
// Invalid URL
|
|
domain = "http://[::1]:namedport"
|
|
_, err = utils.GetCookieDomain(domain)
|
|
assert.ErrorContains(t, err, "parse \"http://[::1]:namedport\": invalid port \":namedport\" after host")
|
|
|
|
// URL with scheme and path
|
|
domain = "https://sub.tinyauth.app/path"
|
|
expected = "tinyauth.app"
|
|
result, err = utils.GetCookieDomain(domain)
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, expected, result)
|
|
|
|
// URL with port
|
|
domain = "http://sub.tinyauth.app:8080"
|
|
expected = "tinyauth.app"
|
|
result, err = utils.GetCookieDomain(domain)
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, expected, result)
|
|
|
|
// Domain managed by ICANN
|
|
domain = "http://example.co.uk"
|
|
_, err = utils.GetCookieDomain(domain)
|
|
assert.Error(t, err, "domain in public suffix list, cannot set cookies")
|
|
}
|
|
|
|
func TestParseFileToLine(t *testing.T) {
|
|
// Normal case
|
|
content := "user1\nuser2\nuser3"
|
|
expected := "user1,user2,user3"
|
|
result := utils.ParseFileToLine(content)
|
|
assert.Equal(t, expected, result)
|
|
|
|
// Case with empty lines and spaces
|
|
content = " user1 \n\n user2 \n user3 \n"
|
|
expected = "user1,user2,user3"
|
|
result = utils.ParseFileToLine(content)
|
|
assert.Equal(t, expected, result)
|
|
|
|
// Case with only empty lines
|
|
content = "\n\n\n"
|
|
expected = ""
|
|
result = utils.ParseFileToLine(content)
|
|
assert.Equal(t, expected, result)
|
|
|
|
// Case with single user
|
|
content = "singleuser"
|
|
expected = "singleuser"
|
|
result = utils.ParseFileToLine(content)
|
|
assert.Equal(t, expected, result)
|
|
|
|
// Case with trailing newline
|
|
content = "user1\nuser2\n"
|
|
expected = "user1,user2"
|
|
result = utils.ParseFileToLine(content)
|
|
assert.Equal(t, expected, result)
|
|
}
|
|
|
|
func TestFilter(t *testing.T) {
|
|
// Normal case
|
|
slice := []int{1, 2, 3, 4, 5}
|
|
testFunc := func(n int) bool { return n%2 == 0 }
|
|
expected := []int{2, 4}
|
|
result := utils.Filter(slice, testFunc)
|
|
assert.DeepEqual(t, expected, result)
|
|
|
|
// Case with no matches
|
|
slice = []int{1, 3, 5}
|
|
testFunc = func(n int) bool { return n%2 == 0 }
|
|
expected = []int{}
|
|
result = utils.Filter(slice, testFunc)
|
|
assert.DeepEqual(t, expected, result)
|
|
|
|
// Case with all matches
|
|
slice = []int{2, 4, 6}
|
|
testFunc = func(n int) bool { return n%2 == 0 }
|
|
expected = []int{2, 4, 6}
|
|
result = utils.Filter(slice, testFunc)
|
|
assert.DeepEqual(t, expected, result)
|
|
|
|
// Case with empty slice
|
|
slice = []int{}
|
|
testFunc = func(n int) bool { return n%2 == 0 }
|
|
expected = []int{}
|
|
result = utils.Filter(slice, testFunc)
|
|
assert.DeepEqual(t, expected, result)
|
|
|
|
// Case with different type (string)
|
|
sliceStr := []string{"apple", "banana", "cherry"}
|
|
testFuncStr := func(s string) bool { return len(s) > 5 }
|
|
expectedStr := []string{"banana", "cherry"}
|
|
resultStr := utils.Filter(sliceStr, testFuncStr)
|
|
assert.DeepEqual(t, expectedStr, resultStr)
|
|
}
|
|
|
|
func TestGetContext(t *testing.T) {
|
|
// Setup
|
|
gin.SetMode(gin.TestMode)
|
|
c, _ := gin.CreateTestContext(nil)
|
|
|
|
// Normal case
|
|
c.Set("context", &config.UserContext{Username: "testuser"})
|
|
result, err := utils.GetContext(c)
|
|
assert.NilError(t, err)
|
|
assert.Equal(t, "testuser", result.Username)
|
|
|
|
// Case with no context
|
|
c.Set("context", nil)
|
|
_, err = utils.GetContext(c)
|
|
assert.Error(t, err, "invalid user context in request")
|
|
|
|
// Case with invalid context type
|
|
c.Set("context", "invalid type")
|
|
_, err = utils.GetContext(c)
|
|
assert.Error(t, err, "invalid user context in request")
|
|
}
|
|
|
|
func TestIsRedirectSafe(t *testing.T) {
|
|
// Setup
|
|
domain := "example.com"
|
|
|
|
// Case with no subdomain
|
|
redirectURL := "http://example.com/welcome"
|
|
result := utils.IsRedirectSafe(redirectURL, domain)
|
|
assert.Equal(t, true, result)
|
|
|
|
// Case with different domain
|
|
redirectURL = "http://malicious.com/phishing"
|
|
result = utils.IsRedirectSafe(redirectURL, domain)
|
|
assert.Equal(t, false, result)
|
|
|
|
// Case with subdomain
|
|
redirectURL = "http://sub.example.com/page"
|
|
result = utils.IsRedirectSafe(redirectURL, domain)
|
|
assert.Equal(t, true, result)
|
|
|
|
// Case with empty redirect URL
|
|
redirectURL = ""
|
|
result = utils.IsRedirectSafe(redirectURL, domain)
|
|
assert.Equal(t, false, result)
|
|
|
|
// Case with invalid URL
|
|
redirectURL = "http://[::1]:namedport"
|
|
result = utils.IsRedirectSafe(redirectURL, domain)
|
|
assert.Equal(t, false, result)
|
|
|
|
// Case with URL having port
|
|
redirectURL = "http://sub.example.com:8080/page"
|
|
result = utils.IsRedirectSafe(redirectURL, domain)
|
|
assert.Equal(t, true, result)
|
|
|
|
// Case with URL having different subdomain
|
|
redirectURL = "http://another.example.com/page"
|
|
result = utils.IsRedirectSafe(redirectURL, domain)
|
|
assert.Equal(t, true, result)
|
|
|
|
// Case with URL having different TLD
|
|
redirectURL = "http://example.org/page"
|
|
result = utils.IsRedirectSafe(redirectURL, domain)
|
|
assert.Equal(t, false, result)
|
|
}
|
|
|
|
func TestIsRedirectSafeMultiLevel(t *testing.T) {
|
|
// Setup
|
|
cookieDomain := "tinyauth.example.com"
|
|
|
|
// Case with 3rd level domain
|
|
redirectURL := "http://tinyauth.example.com/welcome"
|
|
result := utils.IsRedirectSafe(redirectURL, cookieDomain)
|
|
assert.Equal(t, true, result)
|
|
|
|
// Case with root domain
|
|
redirectURL = "http://example.com/unsafe"
|
|
result = utils.IsRedirectSafe(redirectURL, cookieDomain)
|
|
assert.Equal(t, false, result)
|
|
|
|
// Case with 4th level domain
|
|
redirectURL = "http://auth.tinyauth.example.com/post-login"
|
|
result = utils.IsRedirectSafe(redirectURL, cookieDomain)
|
|
assert.Equal(t, true, result)
|
|
|
|
// Case with 5th level domain (should be unsafe)
|
|
redirectURL = "http://x.auth.tinyauth.example.com/deep"
|
|
result = utils.IsRedirectSafe(redirectURL, cookieDomain)
|
|
assert.Equal(t, false, result)
|
|
|
|
// Case with different subdomain
|
|
redirectURL = "http://auth.tinyauth.example.net/attack"
|
|
result = utils.IsRedirectSafe(redirectURL, cookieDomain)
|
|
assert.Equal(t, false, result)
|
|
|
|
// Case with malformed URL
|
|
redirectURL = "http://[::1]:namedport"
|
|
result = utils.IsRedirectSafe(redirectURL, cookieDomain)
|
|
assert.Equal(t, false, result)
|
|
}
|
|
|
|
func TestGetOAuthProvidersConfig(t *testing.T) {
|
|
env := []string{"PROVIDERS_CLIENT1_CLIENTID=client1-id", "PROVIDERS_CLIENT1_CLIENTSECRET=client1-secret"}
|
|
args := []string{"/tinyauth/tinyauth", "--providers-client2-clientid=client2-id", "--providers-client2-clientsecret=client2-secret"}
|
|
|
|
expected := map[string]config.OAuthServiceConfig{
|
|
"client1": {
|
|
ClientID: "client1-id",
|
|
ClientSecret: "client1-secret",
|
|
Name: "Client1",
|
|
},
|
|
"client2": {
|
|
ClientID: "client2-id",
|
|
ClientSecret: "client2-secret",
|
|
Name: "Client2",
|
|
},
|
|
}
|
|
|
|
result, err := utils.GetOAuthProvidersConfig(env, args, "")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, expected, result)
|
|
|
|
// Case with no providers
|
|
env = []string{}
|
|
args = []string{"/tinyauth/tinyauth"}
|
|
expected = map[string]config.OAuthServiceConfig{}
|
|
|
|
result, err = utils.GetOAuthProvidersConfig(env, args, "")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, expected, result)
|
|
|
|
// Case with secret from file
|
|
file, err := os.Create("/tmp/tinyauth_test_file")
|
|
assert.NilError(t, err)
|
|
|
|
_, err = file.WriteString("file content\n")
|
|
assert.NilError(t, err)
|
|
|
|
err = file.Close()
|
|
assert.NilError(t, err)
|
|
defer os.Remove("/tmp/tinyauth_test_file")
|
|
|
|
env = []string{"PROVIDERS_CLIENT1_CLIENTID=client1-id", "PROVIDERS_CLIENT1_CLIENTSECRETFILE=/tmp/tinyauth_test_file"}
|
|
args = []string{"/tinyauth/tinyauth"}
|
|
expected = map[string]config.OAuthServiceConfig{
|
|
"client1": {
|
|
ClientID: "client1-id",
|
|
ClientSecret: "file content",
|
|
Name: "Client1",
|
|
},
|
|
}
|
|
|
|
result, err = utils.GetOAuthProvidersConfig(env, args, "")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, expected, result)
|
|
|
|
// Case with google provider and no redirect URL
|
|
env = []string{"PROVIDERS_GOOGLE_CLIENTID=google-id", "PROVIDERS_GOOGLE_CLIENTSECRET=google-secret"}
|
|
args = []string{"/tinyauth/tinyauth"}
|
|
expected = map[string]config.OAuthServiceConfig{
|
|
"google": {
|
|
ClientID: "google-id",
|
|
ClientSecret: "google-secret",
|
|
RedirectURL: "http://app.url/api/oauth/callback/google",
|
|
Name: "Google",
|
|
},
|
|
}
|
|
|
|
result, err = utils.GetOAuthProvidersConfig(env, args, "http://app.url")
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, expected, result)
|
|
}
|
|
|
|
func TestGetACLS(t *testing.T) {
|
|
// Setup
|
|
env := []string{"TINYAUTH_APPS_APP1_CONFIG_DOMAIN=app1.com", "TINYAUTH_APPS_APP2_CONFIG_DOMAIN=app2.com"}
|
|
args := []string{"--apps-app3-config-domain=app3.com", "--apps-app4-config-domain=app4.com"}
|
|
|
|
expected := config.Apps{
|
|
Apps: map[string]config.App{
|
|
"app1": {
|
|
Config: config.AppConfig{
|
|
Domain: "app1.com",
|
|
},
|
|
},
|
|
"app2": {
|
|
Config: config.AppConfig{
|
|
Domain: "app2.com",
|
|
},
|
|
},
|
|
"app3": {
|
|
Config: config.AppConfig{
|
|
Domain: "app3.com",
|
|
},
|
|
},
|
|
"app4": {
|
|
Config: config.AppConfig{
|
|
Domain: "app4.com",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Test
|
|
result, err := utils.GetACLS(env, args)
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, expected, result)
|
|
}
|