mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-10-24 19:44:03 +02:00
- Introduce configurable OAuth2 configuration cache time via UI and backend
- Refactor OAuth2Router to use `OAuth2ConfigurationCacheTime` with a default of 60s
This commit is contained in:
@@ -93,7 +93,6 @@ var (
|
||||
enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
|
||||
allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
|
||||
enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected")
|
||||
oauth2ConfigurationCache = flag.Duration("oauth2cc", 60*time.Second, "Time in seconds to cache OAuth2 configuration, set to 0 to disable cache. Default: 60 seconds")
|
||||
|
||||
/* Logging Configuration Flags */
|
||||
enableLog = flag.Bool("enablelog", true, "Enable system wide logging, set to false for writing log to STDOUT only")
|
||||
|
||||
@@ -16,6 +16,11 @@ import (
|
||||
"imuslab.com/zoraxy/mod/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultOAuth2ConfigCacheTime defines the default cache duration for OAuth2 configuration
|
||||
DefaultOAuth2ConfigCacheTime = 60 * time.Second
|
||||
)
|
||||
|
||||
type OAuth2RouterOptions struct {
|
||||
OAuth2ServerURL string //The URL of the OAuth 2.0 server server
|
||||
OAuth2TokenURL string //The URL of the OAuth 2.0 token server
|
||||
@@ -25,9 +30,9 @@ type OAuth2RouterOptions struct {
|
||||
OAuth2UserInfoUrl string //The URL of the OAuth 2.0 user info endpoint
|
||||
OAuth2Scopes string //The scopes for OAuth 2.0 Application
|
||||
OAuth2CodeChallengeMethod string //The authorization code challenge method
|
||||
OAuth2ConfigurationCacheTime *time.Duration
|
||||
Logger *logger.Logger
|
||||
Database *database.Database
|
||||
OAuth2ConfigCacheTTL *time.Duration
|
||||
OAuth2ConfigCache *ttlcache.Cache[string, *oauth2.Config]
|
||||
}
|
||||
|
||||
@@ -64,13 +69,20 @@ func NewOAuth2Router(options *OAuth2RouterOptions) *OAuth2Router {
|
||||
options.Database.Read("oauth2", "oauth2UserInfoUrl", &options.OAuth2UserInfoUrl)
|
||||
options.Database.Read("oauth2", "oauth2CodeChallengeMethod", &options.OAuth2CodeChallengeMethod)
|
||||
options.Database.Read("oauth2", "oauth2Scopes", &options.OAuth2Scopes)
|
||||
options.Database.Read("oauth2", "oauth2ConfigurationCacheTime", &options.OAuth2ConfigurationCacheTime)
|
||||
|
||||
ar := &OAuth2Router{
|
||||
options: options,
|
||||
}
|
||||
|
||||
if options.OAuth2ConfigurationCacheTime == nil ||
|
||||
options.OAuth2ConfigurationCacheTime.Seconds() == 0 {
|
||||
cacheTime := DefaultOAuth2ConfigCacheTime
|
||||
options.OAuth2ConfigurationCacheTime = &cacheTime
|
||||
}
|
||||
|
||||
options.OAuth2ConfigCache = ttlcache.New[string, *oauth2.Config](
|
||||
ttlcache.WithTTL[string, *oauth2.Config](*options.OAuth2ConfigCacheTTL),
|
||||
ttlcache.WithTTL[string, *oauth2.Config](*options.OAuth2ConfigurationCacheTime),
|
||||
)
|
||||
go options.OAuth2ConfigCache.Start()
|
||||
|
||||
@@ -102,6 +114,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsGET(w http.ResponseWriter, r *http
|
||||
"oauth2ClientSecret": ar.options.OAuth2ClientSecret,
|
||||
"oauth2ClientId": ar.options.OAuth2ClientId,
|
||||
"oauth2CodeChallengeMethod": ar.options.OAuth2CodeChallengeMethod,
|
||||
"oauth2ConfigurationCacheTime": ar.options.OAuth2ConfigurationCacheTime.String(),
|
||||
})
|
||||
|
||||
utils.SendJSONResponse(w, string(js))
|
||||
@@ -110,6 +123,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsGET(w http.ResponseWriter, r *http
|
||||
func (ar *OAuth2Router) handleSetOAuthSettingsPOST(w http.ResponseWriter, r *http.Request) {
|
||||
//Update the settings
|
||||
var oauth2ServerUrl, oauth2TokenURL, oauth2Scopes, oauth2UserInfoUrl, oauth2CodeChallengeMethod string
|
||||
var oauth2ConfigurationCacheTime *time.Duration
|
||||
|
||||
oauth2ClientId, err := utils.PostPara(r, "oauth2ClientId")
|
||||
if err != nil {
|
||||
@@ -129,8 +143,14 @@ func (ar *OAuth2Router) handleSetOAuthSettingsPOST(w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
oauth2WellKnownUrl, err := utils.PostPara(r, "oauth2WellKnownUrl")
|
||||
oauth2ConfigurationCacheTime, err = utils.PostDuration(r, "oauth2ConfigurationCacheTime")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "oauth2ConfigurationCacheTime not found")
|
||||
return
|
||||
}
|
||||
|
||||
oauth2WellKnownUrl, err := utils.PostPara(r, "oauth2WellKnownUrl")
|
||||
if err != nil || oauth2WellKnownUrl == "" {
|
||||
oauth2ServerUrl, err = utils.PostPara(r, "oauth2ServerUrl")
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "oauth2ServerUrl not found")
|
||||
@@ -167,6 +187,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsPOST(w http.ResponseWriter, r *htt
|
||||
ar.options.OAuth2ClientSecret = oauth2ClientSecret
|
||||
ar.options.OAuth2Scopes = oauth2Scopes
|
||||
ar.options.OAuth2CodeChallengeMethod = oauth2CodeChallengeMethod
|
||||
ar.options.OAuth2ConfigurationCacheTime = oauth2ConfigurationCacheTime
|
||||
|
||||
//Write changes to database
|
||||
ar.options.Database.Write("oauth2", "oauth2WellKnownUrl", oauth2WellKnownUrl)
|
||||
@@ -177,6 +198,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsPOST(w http.ResponseWriter, r *htt
|
||||
ar.options.Database.Write("oauth2", "oauth2ClientSecret", oauth2ClientSecret)
|
||||
ar.options.Database.Write("oauth2", "oauth2Scopes", oauth2Scopes)
|
||||
ar.options.Database.Write("oauth2", "oauth2CodeChallengeMethod", oauth2CodeChallengeMethod)
|
||||
ar.options.Database.Write("oauth2", "oauth2ConfigurationCacheTime", oauth2ConfigurationCacheTime)
|
||||
|
||||
// Flush caches
|
||||
ar.options.OAuth2ConfigCache.DeleteAll()
|
||||
@@ -202,6 +224,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsDELETE(w http.ResponseWriter, r *h
|
||||
ar.options.Database.Delete("oauth2", "oauth2ClientSecret")
|
||||
ar.options.Database.Delete("oauth2", "oauth2Scopes")
|
||||
ar.options.Database.Delete("oauth2", "oauth2CodeChallengeMethod")
|
||||
ar.options.Database.Delete("oauth2", "oauth2ConfigurationCacheTime")
|
||||
|
||||
utils.SendOK(w)
|
||||
}
|
||||
@@ -273,7 +296,7 @@ func (ar *OAuth2Router) HandleOAuth2Auth(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
reqUrl := scheme + "://" + r.Host + r.RequestURI
|
||||
oauthConfigCache, status := ar.options.OAuth2ConfigCache.GetOrSetFunc(r.Host, func() *oauth2.Config {
|
||||
oauthConfigCache, _ := ar.options.OAuth2ConfigCache.GetOrSetFunc(r.Host, func() *oauth2.Config {
|
||||
oauthConfig, err := ar.newOAuth2Conf(scheme + "://" + r.Host + callbackPrefix)
|
||||
if err != nil {
|
||||
ar.options.Logger.PrintAndLog("OAuth2Router", "Failed to fetch OIDC configuration:", err)
|
||||
|
||||
@@ -81,6 +81,26 @@ func PostPara(r *http.Request, key string) (string, error) {
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// Get POST parameter as time.Duration
|
||||
func PostDuration(r *http.Request, key string) (*time.Duration, error) {
|
||||
// Try to parse the form
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Get first value from the form
|
||||
x := r.Form.Get(key)
|
||||
if len(x) == 0 {
|
||||
return nil, errors.New("invalid " + key + " given")
|
||||
}
|
||||
|
||||
duration, err := time.ParseDuration(x)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Join(errors.New("invalid "+key+" duration"), err)
|
||||
}
|
||||
return &duration, nil
|
||||
}
|
||||
|
||||
// Get POST paramter as boolean, accept 1 or true
|
||||
func PostBool(r *http.Request, key string) (bool, error) {
|
||||
x, err := PostPara(r, key)
|
||||
|
||||
@@ -190,7 +190,6 @@ func startupSequence() {
|
||||
oauth2Router = oauth2.NewOAuth2Router(&oauth2.OAuth2RouterOptions{
|
||||
Logger: SystemWideLogger,
|
||||
Database: sysdb,
|
||||
OAuth2ConfigCacheTTL: oauth2ConfigurationCache,
|
||||
})
|
||||
|
||||
//Create a statistic collector
|
||||
|
||||
@@ -158,6 +158,13 @@
|
||||
<input type="text" id="oauth2Scopes" name="oauth2Scopes" placeholder="Enter Scopes">
|
||||
<small>Scopes required by the OAuth2 provider to retrieve information about the authenticated user. Refer to your OAuth2 provider documentation for more information about this. Optional if Well-Known url is configured.</small>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="oauth2ConfigurationCacheTime">Configuration cache time</label>
|
||||
<input type="text" id="oauth2ConfigurationCacheTime" name="oauth2ConfigurationCacheTime" placeholder="Enter Configuration Cache Time">
|
||||
<small>Time to cache OAuth2 configuration before refresh. Accepts Go time.Duration format (e.g. 1m, 10m, 1h). Defaults to 60s.</small>
|
||||
</div>
|
||||
|
||||
<button class="ui basic button" type="submit"><i class="green check icon"></i> Apply Change</button>
|
||||
<button class="ui basic button" type="button" id="oauth2Clear"><i class="red trash icon"></i> Clear</button>
|
||||
</form>
|
||||
@@ -319,6 +326,7 @@
|
||||
$('#oauth2ClientId').val(data.oauth2ClientId);
|
||||
$('#oauth2ClientSecret').val(data.oauth2ClientSecret);
|
||||
$('#oauth2Scopes').val(data.oauth2Scopes);
|
||||
$('#oauth2ConfigurationCacheTime').val(data.oauth2ConfigurationCacheTime);
|
||||
$('[data-value="'+data.oauth2CodeChallengeMethod+'"]').click();
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
|
||||
Reference in New Issue
Block a user