3
This commit is contained in:
parent
5a6eed8c57
commit
5724939500
17 changed files with 576 additions and 356 deletions
|
|
@ -5,16 +5,16 @@ tmp_dir = "tmp"
|
|||
[build]
|
||||
args_bin = []
|
||||
bin = "./tmp/main"
|
||||
cmd = "go build -o ./tmp/main ./cmd/server/."
|
||||
cmd = "go build -buildvcs=false -o ./tmp/main ./cmd/server/"
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata","docker"]
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html","yml","yaml"]
|
||||
include_ext = ["go", "tpl", "tmpl", "html","yml"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
|
|
|
|||
52
.air.toml.bkup
Normal file
52
.air.toml.bkup
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./tmp/main"
|
||||
cmd = "go build -o ./tmp/main ./cmd/server/."
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata","docker"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html","yml","yaml"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
silent = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
app_port = 0
|
||||
enabled = false
|
||||
proxy_port = 0
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
||||
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to Process",
|
||||
"type": "go",
|
||||
"request": "attach",
|
||||
"mode": "local",
|
||||
"processId": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -10,87 +10,85 @@ import (
|
|||
)
|
||||
|
||||
func CallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
config.Wrapper(func(c *config.Config) {
|
||||
configData := config.Get()
|
||||
|
||||
// ctx := context.Background()
|
||||
query := r.URL.Query()
|
||||
|
||||
code := query.Get("code")
|
||||
state := query.Get("state")
|
||||
|
||||
verifier := app.SessionManager.GetString(r.Context(), "code_verifier")
|
||||
verifier := appData.SessionManager.GetString(r.Context(), "code_verifier")
|
||||
if verifier == "" {
|
||||
http.Error(w, "Code verifier not found in session", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
expectedState := app.SessionManager.GetString(r.Context(), "state")
|
||||
expectedState := appData.SessionManager.GetString(r.Context(), "state")
|
||||
if state != expectedState {
|
||||
http.Error(w, "Invalid state parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// originalURL, err := decodeState(state)
|
||||
// if err != nil {
|
||||
// dump.P(err.Error())
|
||||
// http.Error(w, "Invalid state", http.StatusBadRequest)
|
||||
// return
|
||||
// }
|
||||
originalPath := app.SessionManager.GetString(r.Context(), "original_path")
|
||||
originalURL, err := decodeState(state)
|
||||
if err != nil {
|
||||
dump.P(err.Error())
|
||||
http.Error(w, "Invalid state", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
dump.P("Original_Path: " + originalURL)
|
||||
// originalPath := appData.SessionManager.GetString(r.Context(), "original_path")
|
||||
|
||||
authName := c.GetAuthNameByDomain(r.Host)
|
||||
token, fullResponse, e := exchangeCode(code, verifier, c, authName)
|
||||
authName := configData.GetAuthNameByDomain(r.Host)
|
||||
token, fullResponse, e := exchangeCode(code, verifier, authName)
|
||||
if e != nil {
|
||||
dump.Println("exchangeCode: " + e.Error())
|
||||
}
|
||||
|
||||
app.SessionManager.Put(r.Context(), "access_token", token.AccessToken)
|
||||
app.SessionManager.Put(r.Context(), "full_token", fullResponse)
|
||||
appData.SessionManager.Put(r.Context(), "access_token", token.AccessToken)
|
||||
appData.SessionManager.Put(r.Context(), "full_token", fullResponse)
|
||||
|
||||
// SetAuthHeader(w, token.AccessToken)
|
||||
http.Redirect(w, r, originalPath, http.StatusFound)
|
||||
// http.Redirect(w, r, originalURL, http.StatusFound)
|
||||
})
|
||||
// http.Redirect(w, r, originalPath, http.StatusFound)
|
||||
http.Redirect(w, r, originalURL, http.StatusFound)
|
||||
}
|
||||
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
|
||||
config.Wrapper(func(c *config.Config) {
|
||||
configData := config.Get()
|
||||
|
||||
//TODO: only after returninig, delete the session!
|
||||
app.SessionManager.Remove(r.Context(), "access_token")
|
||||
app.SessionManager.Remove(r.Context(), "full_token")
|
||||
appData.SessionManager.Remove(r.Context(), "access_token")
|
||||
appData.SessionManager.Remove(r.Context(), "full_token")
|
||||
|
||||
authName := c.DataMaps.DomainToAuth[r.Host]
|
||||
a := c.Auth[authName]
|
||||
authName := configData.DataMaps.DomainToAuth[r.Host]
|
||||
a := configData.AuthMap[authName]
|
||||
u := a.OpenID.EndPoints.Logout
|
||||
http.Redirect(w, r, u, http.StatusFound)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
config.Wrapper(func(c *config.Config) {
|
||||
configData := config.Get()
|
||||
|
||||
authName := c.DataMaps.DomainToAuth[r.Host]
|
||||
authName := configData.DataMaps.DomainToAuth[r.Host]
|
||||
|
||||
// state := helper.RandStringByBits(64)
|
||||
nonce := helper.RandStringByBits(64)
|
||||
authURL, _ := url.Parse(c.Auth[authName].OpenID.EndPoints.Auth)
|
||||
authURL, _ := url.Parse(configData.AuthMap[authName].OpenID.EndPoints.Auth)
|
||||
query := authURL.Query()
|
||||
|
||||
codeVerifier, _ := generateCodeVerifier()
|
||||
codeChallenge := generateCodeChallenge(codeVerifier)
|
||||
|
||||
originalPath := app.SessionManager.GetString(r.Context(), "original_path")
|
||||
originalPath := appData.SessionManager.GetString(r.Context(), "original_path")
|
||||
state := generateState(url.QueryEscape(originalPath))
|
||||
query.Set("client_id", c.Auth[authName].OpenID.ClientID)
|
||||
query.Set("client_id", configData.AuthMap[authName].OpenID.ClientID)
|
||||
query.Set("response_type", "code")
|
||||
query.Set("scope", "openid")
|
||||
query.Set("redirect_uri", c.Auth[authName].OpenID.RedirectURI)
|
||||
query.Set("redirect_uri", configData.AuthMap[authName].OpenID.RedirectURI)
|
||||
query.Set("code_challenge", codeChallenge)
|
||||
query.Set("code_challenge_method", "S256")
|
||||
query.Set("state", state)
|
||||
query.Set("nonce", nonce)
|
||||
authURL.RawQuery = query.Encode()
|
||||
app.SessionManager.Put(r.Context(), "state", state)
|
||||
app.SessionManager.Put(r.Context(), "code_verifier", codeVerifier)
|
||||
appData.SessionManager.Put(r.Context(), "state", state)
|
||||
appData.SessionManager.Put(r.Context(), "code_verifier", codeVerifier)
|
||||
http.Redirect(w, r, authURL.String(), http.StatusFound)
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,39 +36,46 @@ type EntryPoint struct {
|
|||
type ReverseProxies map[string]ReverseProxy
|
||||
type ReverseProxy *httputil.ReverseProxy
|
||||
|
||||
var app = logic.NewApp()
|
||||
var (
|
||||
appData *logic.App = logic.NewApp()
|
||||
|
||||
// configData *config.Config
|
||||
)
|
||||
|
||||
func main() {
|
||||
helper.AppRunner(func() {
|
||||
config.Wrapper(func(c *config.Config) {
|
||||
configData := config.Get()
|
||||
//////////TESTING AREA//////////////////
|
||||
|
||||
////////////////////////////////////////
|
||||
groups := logic.NewGroups()
|
||||
mainRouter := router.New()
|
||||
groups.ForEach(func(k string, g *logic.Group) {
|
||||
groupSubRouter := mainRouter.Mux.NewRoute().Subrouter()
|
||||
groupSubRouter.Use(Domain_Middleware)
|
||||
groups.ForEach(func(groupName string, g *logic.Group) {
|
||||
mainRouter.AddGroupRouter(groupName)
|
||||
mainRouter.GetGroupRouter(groupName).Mux.Use(Domain_Middleware)
|
||||
for k := range g.ReverseProxies {
|
||||
rpConfig := c.ReverseProxies[k]
|
||||
rpConfig := configData.ReverseProxies[k]
|
||||
domain := rpConfig.Domain
|
||||
proxy := reverse_proxy.New(rpConfig.Host)
|
||||
proxy.Name = domain
|
||||
newRoute := groupSubRouter.NewRoute()
|
||||
subRouter := newRoute.Host(domain).Subrouter()
|
||||
mainRouter.AddHostSubRouter(groupName, domain)
|
||||
if rpConfig.Auth != "" {
|
||||
if _, ok := c.Auth[rpConfig.Auth]; !ok {
|
||||
if _, ok := configData.AuthMap[rpConfig.Auth]; !ok {
|
||||
err := errors.New("Error: Auth " + rpConfig.Auth + " not exist!")
|
||||
panic(err.Error())
|
||||
}
|
||||
pths := c.Auth[rpConfig.Auth].Paths
|
||||
authRoute := subRouter.NewRoute()
|
||||
subRouter.Use(Middleware_SetHeaders)
|
||||
pths := configData.AuthMap[rpConfig.Auth].Paths
|
||||
hostRouterMux := mainRouter.GetHostSubRouter(groupName, domain).Mux
|
||||
hostRouterMux.Use(Middleware_SetHeaders)
|
||||
hostRouterMux.Use(authMiddleware)
|
||||
|
||||
authRoute := hostRouterMux.NewRoute()
|
||||
authSubRouter := authRoute.PathPrefix(pths.Prefix).Subrouter()
|
||||
authSubRouter.Path(pths.Login).Handler(http.HandlerFunc(LoginHandler))
|
||||
authSubRouter.Path(pths.Logout).Handler(http.HandlerFunc(LogoutHandler))
|
||||
authSubRouter.Path(pths.Callback).Handler(http.HandlerFunc(CallbackHandler))
|
||||
subRouter.Use(authMiddleware)
|
||||
}
|
||||
subRouter.PathPrefix("/").Handler(proxy.Httputil)
|
||||
|
||||
mainRouter.GetHostSubRouter(groupName, domain).Mux.PathPrefix("/").Handler(proxy.Httputil)
|
||||
// Filter out static file requests first
|
||||
|
||||
}
|
||||
|
|
@ -79,11 +86,11 @@ func main() {
|
|||
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
// crt, key := "", ""
|
||||
|
||||
crt, key := c.GetCertsPairByDomain(info.ServerName)
|
||||
crt, key := configData.GetCertsPairByDomain(info.ServerName)
|
||||
|
||||
if crt == "" && key == "" {
|
||||
// crt = c.TLS.Certs["default"].Cert
|
||||
// key = c.TLS.Certs["default"].Key
|
||||
// crt = configData.TLS.Certs["default"].Cert
|
||||
// key = configData.TLS.Certs["default"].Key
|
||||
// panic("Error: TLS cert and key not found!")
|
||||
|
||||
}
|
||||
|
|
@ -96,7 +103,7 @@ func main() {
|
|||
}
|
||||
server := &http.Server{
|
||||
Addr: ":" + g.Port,
|
||||
Handler: app.SessionManager.LoadAndSave(groupSubRouter),
|
||||
Handler: appData.SessionManager.LoadAndSave(mainRouter.GetGroupRouter(groupName).Mux),
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
var err error
|
||||
|
|
@ -115,8 +122,8 @@ func main() {
|
|||
}
|
||||
|
||||
})
|
||||
helper.StartTestHTTPServer(3000)
|
||||
})
|
||||
helper.StartTestHTTPServer(3000, "app")
|
||||
helper.StartTestHTTPServer(3001, "oded")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -130,18 +137,19 @@ func main() {
|
|||
|
||||
func authMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
config.Wrapper(func(c *config.Config) {
|
||||
configData := config.Get()
|
||||
currentPath := r.URL.Path
|
||||
authName := c.GetAuthNameByDomain(r.Host)
|
||||
loginPath := c.Auth[authName].Paths.Prefix + c.Auth[authName].Paths.Login
|
||||
logoutPath := c.Auth[authName].Paths.Prefix + c.Auth[authName].Paths.Logout
|
||||
callbackPath := c.Auth[authName].Paths.Prefix + c.Auth[authName].Paths.Callback
|
||||
authName := configData.GetAuthNameByDomain(r.Host)
|
||||
loginPath := configData.AuthMap[authName].Paths.Prefix + configData.AuthMap[authName].Paths.Login
|
||||
logoutPath := configData.AuthMap[authName].Paths.Prefix + configData.AuthMap[authName].Paths.Logout
|
||||
callbackPath := configData.AuthMap[authName].Paths.Prefix + configData.AuthMap[authName].Paths.Callback
|
||||
// TODO: mark auth reverse proxy in yaml
|
||||
|
||||
if r.Host == "keycloak.z.com" {
|
||||
proxyName := configData.GetProxyNameByDomain(r.Host)
|
||||
if configData.ReverseProxies[proxyName].IsAuthServer {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
switch currentPath {
|
||||
case loginPath:
|
||||
{
|
||||
|
|
@ -163,14 +171,15 @@ func authMiddleware(next http.Handler) http.Handler {
|
|||
default:
|
||||
{
|
||||
|
||||
accessToken := app.SessionManager.GetString(r.Context(), "access_token")
|
||||
accessToken := appData.SessionManager.GetString(r.Context(), "access_token")
|
||||
if accessToken == "" {
|
||||
authName := c.DataMaps.DomainToAuth[r.Host]
|
||||
http.Redirect(w, r, c.Auth[authName].Paths.Prefix+c.Auth[authName].Paths.Login, http.StatusFound)
|
||||
|
||||
authName := configData.DataMaps.DomainToAuth[r.Host]
|
||||
http.Redirect(w, r, configData.AuthMap[authName].Paths.Prefix+configData.AuthMap[authName].Paths.Login, http.StatusFound)
|
||||
return
|
||||
}
|
||||
// auth.SetAuthHeader(w, accessToken)
|
||||
a := c.Auth[authName]
|
||||
a := configData.AuthMap[authName]
|
||||
pths := a.Paths
|
||||
prefix := pths.Prefix
|
||||
login := pths.Login
|
||||
|
|
@ -182,7 +191,8 @@ func authMiddleware(next http.Handler) http.Handler {
|
|||
// return
|
||||
}
|
||||
|
||||
tokenOk := IsAuthorizedJWT(accessToken, c, "default")
|
||||
tokenOk := IsAuthorizedJWT(accessToken, authName)
|
||||
|
||||
if !tokenOk {
|
||||
http.Redirect(w, r, loginPath, http.StatusFound)
|
||||
return
|
||||
|
|
@ -191,7 +201,6 @@ func authMiddleware(next http.Handler) http.Handler {
|
|||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -200,8 +209,8 @@ func Domain_Middleware(next http.Handler) http.Handler {
|
|||
// c := config.Get()
|
||||
|
||||
// requestedPath := r.URL.Path
|
||||
// authName := c.GetAuthNameByDomain(r.Host)
|
||||
// auth := c.Auth[authName]
|
||||
// authName := configData.GetAuthNameByDomain(r.Host)
|
||||
// auth := configData.AuthMap[authName]
|
||||
// excludedPaths := []string{
|
||||
// auth.Paths.Prefix + auth.Paths.Login,
|
||||
// auth.Paths.Prefix + auth.Paths.Callback,
|
||||
|
|
@ -210,7 +219,7 @@ func Domain_Middleware(next http.Handler) http.Handler {
|
|||
// contains := helper.IsSliceContains(excludedPaths, requestedPath)
|
||||
// contains := slices.Contains(excludedPaths, requestedPath)
|
||||
// if !contains {
|
||||
// app.SessionManager.Put(r.Context(), "original_path", requestedPath)
|
||||
// appData.SessionManager.Put(r.Context(), "original_path", requestedPath)
|
||||
// }
|
||||
// dump.P(requestedPath)
|
||||
|
||||
|
|
@ -303,12 +312,22 @@ func decodeState(encodedState string) (string, error) {
|
|||
}
|
||||
return stateData["redirect_uri"], nil
|
||||
}
|
||||
func exchangeCode(code string, verifier string, c *config.Config, authName string) (*TokenResponse, string, error) {
|
||||
|
||||
func exchangeCode(code string, verifier string, authName string) (*TokenResponse, string, error) {
|
||||
configData := config.Get()
|
||||
clientID := configData.AuthMap[authName].OpenID.ClientID
|
||||
clientSecert := configData.AuthMap[authName].OpenID.ClientSecert
|
||||
redirectURI := configData.AuthMap[authName].OpenID.RedirectURI
|
||||
// if strings.Contains(redirectURI,"<{{dynamic}}>") {
|
||||
// configData.GenerateDynamicRedirectUri()
|
||||
// }
|
||||
|
||||
// GenerateDynamicRedirectUri
|
||||
data := url.Values{}
|
||||
data.Set("grant_type", "authorization_code")
|
||||
data.Set("client_id", c.Auth[authName].OpenID.ClientID)
|
||||
data.Set("client_secret", c.Auth[authName].OpenID.ClientSecert)
|
||||
data.Set("redirect_uri", c.Auth[authName].OpenID.RedirectURI)
|
||||
data.Set("client_id", clientID)
|
||||
data.Set("client_secret", clientSecert)
|
||||
data.Set("redirect_uri", redirectURI)
|
||||
data.Set("code", code)
|
||||
data.Set("scope", "openid zapp")
|
||||
if verifier != "" {
|
||||
|
|
@ -318,7 +337,7 @@ func exchangeCode(code string, verifier string, c *config.Config, authName strin
|
|||
TLSClientConfig: &tls.Config{InsecureSkipVerify: DEVELOPMENT},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
u := c.Auth[authName].OpenID.EndPoints.Token
|
||||
u := configData.AuthMap[authName].OpenID.EndPoints.Token
|
||||
r, _ := http.NewRequest(http.MethodPost, u, strings.NewReader(data.Encode()))
|
||||
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
resp, err := client.Do(r)
|
||||
|
|
@ -358,7 +377,9 @@ func generateCodeChallenge(verifier string) string {
|
|||
return base64.RawURLEncoding.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func IsAuthorizedJWT(rawAccessToken string, c *config.Config, authName string) bool {
|
||||
func IsAuthorizedJWT(rawAccessToken string, authName string) bool {
|
||||
configData := config.Get()
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
|
@ -367,14 +388,14 @@ func IsAuthorizedJWT(rawAccessToken string, c *config.Config, authName string) b
|
|||
Transport: tr,
|
||||
}
|
||||
ctx := oidc.ClientContext(context.Background(), client)
|
||||
provider, err := oidc.NewProvider(ctx, c.Auth[authName].OpenID.EndPoints.Issuer)
|
||||
provider, err := oidc.NewProvider(ctx, configData.AuthMap[authName].OpenID.EndPoints.Issuer)
|
||||
if err != nil {
|
||||
dump.Println("authorisation failed while getting the provider: " + err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
oidcConfig := &oidc.Config{
|
||||
ClientID: c.Auth[authName].OpenID.ClientID,
|
||||
ClientID: configData.AuthMap[authName].OpenID.ClientID,
|
||||
}
|
||||
verifier := provider.Verifier(oidcConfig)
|
||||
idToken, err := verifier.Verify(ctx, rawAccessToken)
|
||||
|
|
@ -394,22 +415,22 @@ func IsAuthorizedJWT(rawAccessToken string, c *config.Config, authName string) b
|
|||
///////////////////////////////////
|
||||
///////////////////////////////////
|
||||
|
||||
func isStaticFileRequest(path string) bool {
|
||||
// Check for common static file prefixes
|
||||
if strings.HasPrefix(path, "/static/") || strings.HasPrefix(path, "/assets/") {
|
||||
return true
|
||||
}
|
||||
// func isStaticFileRequest(path string) bool {
|
||||
// // Check for common static file prefixes
|
||||
// if strings.HasPrefix(path, "/static/") || strings.HasPrefix(path, "/assets/") {
|
||||
// return true
|
||||
// }
|
||||
|
||||
// Check for common static file extensions
|
||||
staticExtensions := []string{
|
||||
".css", ".js", ".jpg", ".jpeg", ".png", ".gif", ".svg", ".ico",
|
||||
}
|
||||
// // Check for common static file extensions
|
||||
// staticExtensions := []string{
|
||||
// ".css", ".js", ".jpg", ".jpeg", ".png", ".gif", ".svg", ".ico",
|
||||
// }
|
||||
|
||||
for _, ext := range staticExtensions {
|
||||
if strings.HasSuffix(path, ext) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// for _, ext := range staticExtensions {
|
||||
// if strings.HasSuffix(path, ext) {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
|
||||
return false
|
||||
}
|
||||
// return false
|
||||
// }
|
||||
|
|
|
|||
44
config.yml
44
config.yml
|
|
@ -8,7 +8,6 @@ reverse_proxies:
|
|||
enabled: true
|
||||
certs: default
|
||||
auth_server: true
|
||||
|
||||
app:
|
||||
domain: app.z.com
|
||||
host: http://127.0.0.1:3000
|
||||
|
|
@ -16,7 +15,16 @@ reverse_proxies:
|
|||
tls:
|
||||
enabled: true
|
||||
certs: default
|
||||
auth: default
|
||||
auth: app_auth
|
||||
oded:
|
||||
domain: oded.z.com
|
||||
host: http://127.0.0.1:3001
|
||||
entry_point: https
|
||||
tls:
|
||||
enabled: true
|
||||
certs: default
|
||||
auth: oded_auth
|
||||
|
||||
|
||||
tls:
|
||||
certs:
|
||||
|
|
@ -32,7 +40,7 @@ entry_points:
|
|||
port: 80
|
||||
|
||||
auth:
|
||||
default:
|
||||
app_auth:
|
||||
paths:
|
||||
prefix: /auth
|
||||
login: /login
|
||||
|
|
@ -43,19 +51,25 @@ auth:
|
|||
realm: dev
|
||||
client_id: dev_client
|
||||
client_secret: dWhSJgARBAuBAXN7sUTpqpIq2sKQdugs
|
||||
redirect_uri: https://app.z.com/auth/callback
|
||||
redirect_uri: <{{dynamic}}>/auth/callback
|
||||
post_logout_redirect_uri: https://app.z.com/auth/logout
|
||||
config_path: /realms/{{realm}}/.well-known/openid-configuration
|
||||
# config_fields:
|
||||
# - issuer
|
||||
# - authorization_endpoint
|
||||
# - token_endpoint
|
||||
# - introspection_endpoint
|
||||
# - userinfo_endpoint
|
||||
# - end_session_endpoint
|
||||
# - jwks_uri
|
||||
# issuer: http://127.0.0.1:8080/realms/dev
|
||||
|
||||
config_path: /realms/<{{realm}}>/.well-known/openid-configuration
|
||||
oded_auth:
|
||||
paths:
|
||||
prefix: /auth
|
||||
login: /login
|
||||
logout: /logout
|
||||
callback: /callback
|
||||
open_id:
|
||||
host: http://127.0.0.1:8080
|
||||
realm: dev
|
||||
client_id: dev_client
|
||||
client_secret: dWhSJgARBAuBAXN7sUTpqpIq2sKQdugs
|
||||
# redirect_uri: https://oded.z.com/auth/callback
|
||||
redirect_uri: <{{dynamic}}>/auth/callback
|
||||
post_logout_redirect_uri: https://oded.z.com/auth/logout
|
||||
# post_logout_redirect_uri: <{{dynamic}}>/auth/logout
|
||||
config_path: /realms/<{{realm}}>/.well-known/openid-configuration
|
||||
|
||||
|
||||
# scope: openid profile email
|
||||
|
|
|
|||
|
|
@ -15,20 +15,28 @@ const Viper_File_Name string = "config"
|
|||
const Viper_File_Type string = "yaml"
|
||||
const Viper_File_Path string = "."
|
||||
|
||||
var c *Config
|
||||
var (
|
||||
configData *Config
|
||||
)
|
||||
|
||||
func init() {
|
||||
helper.New().Screen.Clear()
|
||||
c = createConfig()
|
||||
|
||||
h := helper.New()
|
||||
h.Screen.Clear()
|
||||
configData = createConfig()
|
||||
}
|
||||
|
||||
func Wrapper(fn func(c *Config)) {
|
||||
fn(c)
|
||||
fn(configData)
|
||||
}
|
||||
func Get() *Config {
|
||||
return c
|
||||
|
||||
return configData
|
||||
}
|
||||
func Get2() *Config {
|
||||
return configData
|
||||
}
|
||||
func createConfig() *Config {
|
||||
|
||||
c := &Config{}
|
||||
c.initViper()
|
||||
c.setViperOptions(Viper_File_Name, Viper_File_Type, Viper_File_Path)
|
||||
|
|
@ -76,6 +84,19 @@ func (rp *ReverseProxies) ForEach(fn func(rpName string, rpConfig *ReverseProxy)
|
|||
}
|
||||
}
|
||||
|
||||
func (rp *ReverseProxies) ForEachReturn(fn func(rpName string, rpConfig *ReverseProxy) (res ForEachReturn)) *ForEachReturn {
|
||||
res := ForEachReturn{}
|
||||
for rpName, rpData := range *rp {
|
||||
res = append(fn(rpName, &rpData), res)
|
||||
}
|
||||
return &res
|
||||
}
|
||||
|
||||
// func (rp AuthMap) ForEach(fn func(authName string, authConfig Auth)) {
|
||||
// for authName, authConfig := range rp {
|
||||
// fn(authName, authConfig)
|
||||
// }
|
||||
// }
|
||||
func (c *Config) fetchEndPointsByAuthName(authName string) map[string]any {
|
||||
configURL := c.KeycloakWellknownURL(authName)
|
||||
resp := helper.IsFetchOK[EndPoints](configURL, "", http.Get)
|
||||
|
|
@ -89,10 +110,10 @@ func (c *Config) fetchEndPointsByAuthName(authName string) map[string]any {
|
|||
}
|
||||
|
||||
func (c *Config) initOpenIDEndPoints() {
|
||||
for authName := range c.Auth {
|
||||
for authName := range c.AuthMap {
|
||||
data := c.fetchEndPointsByAuthName(authName)
|
||||
|
||||
c.Auth[authName].OpenID.EndPoints = &EndPoints{
|
||||
c.AuthMap[authName].OpenID.EndPoints = &EndPoints{
|
||||
Issuer: data["issuer"].(string),
|
||||
Auth: data["authorization_endpoint"].(string),
|
||||
Introspection: data["introspection_endpoint"].(string),
|
||||
|
|
@ -107,17 +128,35 @@ func (c *Config) initOpenIDEndPoints() {
|
|||
}
|
||||
func (c *Config) KeycloakWellknownURL(authName string) string {
|
||||
|
||||
if _, ok := c.Auth[authName]; !ok {
|
||||
if _, ok := c.AuthMap[authName]; !ok {
|
||||
return ""
|
||||
}
|
||||
hostUrl := c.Auth[authName].OpenID.Host
|
||||
realm := c.Auth[authName].OpenID.Realm
|
||||
configPath := c.Auth[authName].OpenID.ConfigPath
|
||||
hostUrl := c.AuthMap[authName].OpenID.Host
|
||||
realm := c.AuthMap[authName].OpenID.Realm
|
||||
configPath := c.AuthMap[authName].OpenID.ConfigPath
|
||||
u := hostUrl
|
||||
u += strings.ReplaceAll(configPath, "{{realm}}", realm)
|
||||
u += strings.ReplaceAll(configPath, "<{{realm}}>", realm)
|
||||
return u
|
||||
}
|
||||
|
||||
func (c *Config) GenerateDynamicRedirectUri(proxyName string, authName string) string {
|
||||
if _, ok := c.AuthMap[authName]; !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
proto := "http"
|
||||
|
||||
domain := c.ReverseProxies[proxyName].Domain
|
||||
tls := c.ReverseProxies[proxyName].TLS.Enabled
|
||||
if tls {
|
||||
proto = "https"
|
||||
}
|
||||
redirectURI := c.AuthMap[authName].OpenID.RedirectURI
|
||||
u := redirectURI
|
||||
u = strings.ReplaceAll(redirectURI, "<{{dynamic}}>", proto+"://"+domain)
|
||||
|
||||
return u
|
||||
}
|
||||
func (c *Config) initDomainToProxyNameMap() {
|
||||
mp := make(map[string]string)
|
||||
c.ReverseProxies.ForEach(func(rpName string, rpConfig *ReverseProxy) {
|
||||
|
|
@ -126,17 +165,22 @@ func (c *Config) initDomainToProxyNameMap() {
|
|||
c.DataMaps.DomainToProxy = mp
|
||||
}
|
||||
func (c *Config) initDomainToProxyAuthName() {
|
||||
|
||||
authMap := map[string]int{}
|
||||
for authName := range c.Auth {
|
||||
for authName := range c.AuthMap {
|
||||
authMap[authName] = 1
|
||||
}
|
||||
|
||||
mp := make(map[string]string)
|
||||
c.ReverseProxies.ForEach(func(rpName string, rpConfig *ReverseProxy) {
|
||||
if v, ok := authMap[rpConfig.Auth]; ok && v == 1 {
|
||||
mp[rpConfig.Domain] = rpConfig.Auth
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
c.DataMaps.DomainToAuth = mp
|
||||
|
||||
}
|
||||
func (c *Config) initDomainToCertName() {
|
||||
crtMap := map[string]int{}
|
||||
|
|
@ -155,6 +199,7 @@ func (c *Config) initDomainToCertName() {
|
|||
func (c *Config) GetAuthNameByDomain(domain string) string {
|
||||
return c.DataMaps.DomainToAuth[domain]
|
||||
}
|
||||
|
||||
func (c *Config) GetProxyNameByDomain(domain string) string {
|
||||
return c.DataMaps.DomainToProxy[domain]
|
||||
}
|
||||
|
|
@ -174,5 +219,25 @@ func (c *Config) GetCertsPairByDomain(domain string) (string, string) {
|
|||
}
|
||||
|
||||
return crt, key
|
||||
|
||||
}
|
||||
|
||||
func (c *Config) GetProxiesSliceByAuthName(authName string) []string {
|
||||
slc := &[]string{}
|
||||
c.ReverseProxies.ForEach(func(rpName string, rpConfig *ReverseProxy) {
|
||||
isOK := true
|
||||
if authName != rpConfig.Auth {
|
||||
isOK = false
|
||||
}
|
||||
if _, ok := c.AuthMap[authName]; !ok {
|
||||
isOK = false
|
||||
}
|
||||
|
||||
if isOK {
|
||||
helper.AppendToPointer(slc, rpName)
|
||||
}
|
||||
})
|
||||
return *slc
|
||||
}
|
||||
func (c *Config) GetAuthNameByProxyName(proxyName string) string {
|
||||
return c.ReverseProxies[proxyName].Auth
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
package config
|
||||
|
||||
import "github.com/spf13/viper"
|
||||
|
||||
type Config struct {
|
||||
Viper *viper.Viper
|
||||
DataMaps DataMaps
|
||||
ReverseProxies ReverseProxies `mapstructure:"reverse_proxies"`
|
||||
TLS TLS `mapstructure:"tls"`
|
||||
EntryPoints EntryPoints `mapstructure:"entry_points"`
|
||||
Auth map[string]*AuthInstance `mapstructure:"auth"`
|
||||
}
|
||||
|
||||
type DataMaps struct {
|
||||
DomainToProxy map[string]string
|
||||
DomainToAuth map[string]string
|
||||
DomainToCert map[string]string
|
||||
}
|
||||
|
||||
// AUTH
|
||||
// type Auth map[string]AuthInstance
|
||||
|
||||
type AuthInstance struct {
|
||||
Paths Paths `mapstructure:"paths"`
|
||||
OpenID OpenID `mapstructure:"open_id"`
|
||||
}
|
||||
type Paths struct {
|
||||
Prefix string `mapstructure:"prefix"`
|
||||
Login string `mapstructure:"login"`
|
||||
Logout string `mapstructure:"logout"`
|
||||
Callback string `mapstructure:"callback"`
|
||||
}
|
||||
type OpenID struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Realm string `mapstructure:"realm"`
|
||||
ClientID string `mapstructure:"client_id"`
|
||||
ClientSecert string `mapstructure:"client_secret"`
|
||||
RedirectURI string `mapstructure:"redirect_uri"`
|
||||
PostLogoutRedirectURI string `mapstructure:"post_logout_redirect_uri"`
|
||||
ConfigPath string `mapstructure:"config_path"`
|
||||
EndPoints *EndPoints
|
||||
}
|
||||
|
||||
type EndPoints struct {
|
||||
Issuer string
|
||||
Auth string
|
||||
Introspection string
|
||||
Token string
|
||||
UserInfo string
|
||||
Logout string
|
||||
JwksUri string
|
||||
}
|
||||
|
||||
// type OpenIdEndPoints struct {
|
||||
// Issuer string
|
||||
// Authorization string
|
||||
// Token string
|
||||
// Introspection string
|
||||
// UserInfo string
|
||||
// EndSession string
|
||||
// }
|
||||
|
||||
// ReverseProxies
|
||||
|
||||
type ReverseProxies map[string]ReverseProxy
|
||||
|
||||
type ReverseProxy struct {
|
||||
Domain string `mapstructure:"domain"`
|
||||
Host string `mapstructure:"host"`
|
||||
EntryPoint string `mapstructure:"entry_point"`
|
||||
TLS TLS_RP `mapstructure:"tls"`
|
||||
Auth string `mapstructure:"auth"`
|
||||
}
|
||||
type TLS_RP struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
Certs string `mapstructure:"certs"`
|
||||
}
|
||||
|
||||
// TLS
|
||||
type TLS struct {
|
||||
Certs map[string]Certs `mapstructure:"certs"`
|
||||
}
|
||||
type Certs struct {
|
||||
Cert string `mapstructure:"cert"`
|
||||
Key string `mapstructure:"key"`
|
||||
}
|
||||
|
||||
// EntryPoints
|
||||
type EntryPoints map[string]EntryPoint
|
||||
type EntryPoint struct {
|
||||
Port string `mapstructure:"port"`
|
||||
TLS bool `mapstructure:"tls"`
|
||||
}
|
||||
36
internal/config/types_auth_.go
Normal file
36
internal/config/types_auth_.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package config
|
||||
|
||||
type AuthMap map[string]*Auth
|
||||
|
||||
type Auth struct {
|
||||
Paths Paths `mapstructure:"paths"`
|
||||
OpenID OpenID `mapstructure:"open_id"`
|
||||
}
|
||||
|
||||
type Paths struct {
|
||||
Prefix string `mapstructure:"prefix"`
|
||||
Login string `mapstructure:"login"`
|
||||
Logout string `mapstructure:"logout"`
|
||||
Callback string `mapstructure:"callback"`
|
||||
}
|
||||
|
||||
type OpenID struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Realm string `mapstructure:"realm"`
|
||||
ClientID string `mapstructure:"client_id"`
|
||||
ClientSecert string `mapstructure:"client_secret"`
|
||||
RedirectURI string `mapstructure:"redirect_uri"`
|
||||
PostLogoutRedirectURI string `mapstructure:"post_logout_redirect_uri"`
|
||||
ConfigPath string `mapstructure:"config_path"`
|
||||
EndPoints *EndPoints
|
||||
}
|
||||
|
||||
type EndPoints struct {
|
||||
Issuer string
|
||||
Auth string
|
||||
Introspection string
|
||||
Token string
|
||||
UserInfo string
|
||||
Logout string
|
||||
JwksUri string
|
||||
}
|
||||
20
internal/config/types_config.go
Normal file
20
internal/config/types_config.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package config
|
||||
|
||||
import "github.com/spf13/viper"
|
||||
|
||||
type Config struct {
|
||||
Viper *viper.Viper
|
||||
DataMaps DataMaps
|
||||
ReverseProxies ReverseProxies `mapstructure:"reverse_proxies"`
|
||||
TLS TLS `mapstructure:"tls"`
|
||||
EntryPoints EntryPoints `mapstructure:"entry_points"`
|
||||
AuthMap AuthMap `mapstructure:"auth"`
|
||||
}
|
||||
|
||||
type DataMaps struct {
|
||||
DomainToProxy map[string]string
|
||||
DomainToAuth map[string]string
|
||||
DomainToCert map[string]string
|
||||
}
|
||||
|
||||
type ForEachReturn []any
|
||||
8
internal/config/types_entry_points.go
Normal file
8
internal/config/types_entry_points.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package config
|
||||
|
||||
type EntryPoints map[string]EntryPoint
|
||||
|
||||
type EntryPoint struct {
|
||||
Port string `mapstructure:"port"`
|
||||
TLS bool `mapstructure:"tls"`
|
||||
}
|
||||
12
internal/config/types_reverse_proxy.go
Normal file
12
internal/config/types_reverse_proxy.go
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package config
|
||||
|
||||
type ReverseProxies map[string]ReverseProxy
|
||||
|
||||
type ReverseProxy struct {
|
||||
Domain string `mapstructure:"domain"`
|
||||
Host string `mapstructure:"host"`
|
||||
EntryPoint string `mapstructure:"entry_point"`
|
||||
TLS TLS_RP `mapstructure:"tls"`
|
||||
Auth string `mapstructure:"auth"`
|
||||
IsAuthServer bool `mapstructure:"auth_server"`
|
||||
}
|
||||
15
internal/config/types_tls.go
Normal file
15
internal/config/types_tls.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package config
|
||||
|
||||
type TLS struct {
|
||||
Certs map[string]Certs `mapstructure:"certs"`
|
||||
}
|
||||
|
||||
type Certs struct {
|
||||
Cert string `mapstructure:"cert"`
|
||||
Key string `mapstructure:"key"`
|
||||
}
|
||||
|
||||
type TLS_RP struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
Certs string `mapstructure:"certs"`
|
||||
}
|
||||
|
|
@ -1,24 +1,77 @@
|
|||
package router
|
||||
|
||||
import "github.com/gorilla/mux"
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"zeevdiukman.com/zprox/pkg/helper"
|
||||
)
|
||||
|
||||
func New() *MainRouter {
|
||||
r := &MainRouter{}
|
||||
r.SetMux()
|
||||
r.setMux()
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *MainRouter) SetMux() {
|
||||
func (r *MainRouter) setMux() {
|
||||
r.Mux = mux.NewRouter()
|
||||
}
|
||||
func (mainRouter *MainRouter) AddGroupRouter(groupName string) *GroupRouter {
|
||||
s := mainRouter.Mux.NewRoute().Subrouter()
|
||||
groupRouter := &GroupRouter{
|
||||
Mux: s,
|
||||
}
|
||||
|
||||
newMap := make(GroupRouters)
|
||||
helper.MapIter(mainRouter.GroupRouters, func(grName string, gr *GroupRouter) {
|
||||
newMap[grName] = gr
|
||||
})
|
||||
newMap[groupName] = groupRouter
|
||||
|
||||
mainRouter.GroupRouters = newMap
|
||||
return groupRouter
|
||||
}
|
||||
func (mainRouter *MainRouter) GetGroupRouter(groupName string) *GroupRouter {
|
||||
return mainRouter.GroupRouters[groupName]
|
||||
}
|
||||
func (mainRouter *MainRouter) GetHostSubRouter(groupName string, domain string) *SubRouter {
|
||||
return mainRouter.GroupRouters[groupName].SubRouters[domain]
|
||||
}
|
||||
func (mainRouter *MainRouter) AddHostSubRouter(groupName string, domain string) *SubRouter {
|
||||
groupSubRouter := mainRouter.GroupRouters[groupName]
|
||||
newRoute := groupSubRouter.Mux.NewRoute()
|
||||
hostSubRouter := &SubRouter{
|
||||
Mux: newRoute.Host(domain).Subrouter(),
|
||||
}
|
||||
newMap := make(SubRouters)
|
||||
helper.MapIter(mainRouter.GroupRouters[groupName].SubRouters, func(grName string, gr *SubRouter) {
|
||||
newMap[grName] = gr
|
||||
})
|
||||
newMap[domain] = hostSubRouter
|
||||
mainRouter.GroupRouters[groupName].SubRouters = newMap
|
||||
return hostSubRouter
|
||||
}
|
||||
|
||||
func (mainRouter *MainRouter) AddGroupSubRouter(groupName string, subRouterName string) *SubRouter {
|
||||
gsr := mainRouter.GetGroupRouter(groupName).Mux.NewRoute().Subrouter()
|
||||
subRouter := &SubRouter{
|
||||
Mux: gsr,
|
||||
}
|
||||
mainRouter.GroupRouters[groupName].SubRouters[subRouterName] = subRouter
|
||||
return subRouter
|
||||
}
|
||||
|
||||
type MainRouter struct {
|
||||
Subrouters Subrouters
|
||||
GroupRouters GroupRouters
|
||||
Mux *mux.Router
|
||||
}
|
||||
type Subrouters map[string]SubRouter
|
||||
type SubRouter struct {
|
||||
Name string
|
||||
Group string
|
||||
*mux.Router
|
||||
|
||||
type GroupRouters map[string]*GroupRouter
|
||||
|
||||
type GroupRouter struct {
|
||||
SubRouters SubRouters
|
||||
Mux *mux.Router
|
||||
}
|
||||
|
||||
type SubRouters map[string]*SubRouter
|
||||
type SubRouter struct {
|
||||
Mux *mux.Router
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ func CapitalizeFirstLetter(input string) string {
|
|||
|
||||
return string(runes) // Convert rune slice back to string
|
||||
}
|
||||
func MapIter[K comparable, V comparable](m map[K]V, fn func(K, V)) {
|
||||
func MapIter[K string, V any](m map[K]V, fn func(K, V)) {
|
||||
for p, d := range m {
|
||||
fn(p, d)
|
||||
}
|
||||
|
|
@ -456,25 +456,25 @@ func AppRunner(runApp func()) {
|
|||
<-ctx.Done()
|
||||
//do stuff after ending
|
||||
wg.Wait()
|
||||
fmt.Println("BYE BYE!")
|
||||
log.Println("AppRunner stopped")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func StartTestHTTPServer(port int) {
|
||||
func StartTestHTTPServer(port int, name string) {
|
||||
p := strconv.Itoa(port)
|
||||
go func() {
|
||||
log.Println("Test server is running at http://" + GetIP() + ":" + p)
|
||||
r := mux.NewRouter()
|
||||
r.Path("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "/ OK")
|
||||
fmt.Fprintln(w, r.Host+"/ OK")
|
||||
})
|
||||
r.Path("/test1").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "/test1 OK")
|
||||
fmt.Fprintln(w, r.Host+"/test1 OK")
|
||||
})
|
||||
r.Path("/test2").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "/test2 OK")
|
||||
fmt.Fprintln(w, r.Host+"/test2 OK")
|
||||
})
|
||||
err := http.ListenAndServe(":3000", r)
|
||||
err := http.ListenAndServe(":"+p, r)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
|
|
@ -510,3 +510,7 @@ func GetIP(prefix ...string) string {
|
|||
}
|
||||
return prfx
|
||||
}
|
||||
|
||||
func AppendToPointer[T any](slc *[]T, val T) {
|
||||
*slc = append(*slc, val)
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
BIN
tmp/main
BIN
tmp/main
Binary file not shown.
Loading…
Reference in a new issue