mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-08 00:26:37 +08:00
OIDC
This commit is contained in:
@@ -21,6 +21,9 @@ func SetApiRouter(router *gin.Engine) {
|
||||
apiRouter.GET("/oauth/wechat", middleware.CriticalRateLimit(), controller.WeChatAuth)
|
||||
apiRouter.GET("/oauth/wechat/bind", middleware.CriticalRateLimit(), middleware.UserAuth(), controller.WeChatBind)
|
||||
apiRouter.GET("/oauth/email/bind", middleware.CriticalRateLimit(), middleware.UserAuth(), controller.EmailBind)
|
||||
apiRouter.GET("/oauth/:source/authorize", middleware.CriticalRateLimit(), controller.OAuthAuthorize)
|
||||
apiRouter.GET("/oauth/:source/callback", middleware.CriticalRateLimit(), controller.OAuthCallback)
|
||||
apiRouter.POST("/oauth/link-existing", middleware.CriticalRateLimit(), controller.LinkExistingOAuthAccount)
|
||||
|
||||
userRoute := apiRouter.Group("/user")
|
||||
{
|
||||
@@ -58,6 +61,15 @@ func SetApiRouter(router *gin.Engine) {
|
||||
optionRoute.POST("/geoip/lookup", controller.LookupGeoIP)
|
||||
optionRoute.POST("/database/cleanup", controller.CleanupDatabaseObservability)
|
||||
}
|
||||
authSourceRoute := apiRouter.Group("/auth-sources")
|
||||
authSourceRoute.Use(middleware.RootAuth(), middleware.NoTokenAuth())
|
||||
{
|
||||
authSourceRoute.GET("/", controller.ListAuthSources)
|
||||
authSourceRoute.POST("/", controller.CreateAuthSource)
|
||||
authSourceRoute.POST("/:id/update", controller.UpdateAuthSource)
|
||||
authSourceRoute.POST("/:id/delete", controller.DeleteAuthSource)
|
||||
authSourceRoute.POST("/:id/toggle", controller.ToggleAuthSource)
|
||||
}
|
||||
updateRoute := apiRouter.Group("/update")
|
||||
updateRoute.Use(middleware.RootAuth(), middleware.NoTokenAuth())
|
||||
{
|
||||
|
||||
@@ -201,6 +201,74 @@ func TestPhase2BatchOptionUpdateValidatesMergedState(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthSourceUpdateAcceptsClientSecret(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
common.RedisEnabled = false
|
||||
setupTestDB(t)
|
||||
|
||||
engine := gin.New()
|
||||
engine.Use(sessions.Sessions("session", cookie.NewStore([]byte("test-secret"))))
|
||||
router.SetApiRouter(engine)
|
||||
|
||||
loginCookie := loginAsRoot(t, engine)
|
||||
|
||||
createResp := performSessionJSONRequest(t, engine, loginCookie, http.MethodPost, "/api/auth-sources/", map[string]any{
|
||||
"name": "GitHub",
|
||||
"type": "github",
|
||||
"display_name": "GitHub",
|
||||
"is_active": false,
|
||||
"client_id": "github-client-id",
|
||||
"client_secret": "initial-secret",
|
||||
"scopes": "user:email",
|
||||
})
|
||||
|
||||
var created model.AuthSource
|
||||
decodeResponseData(t, createResp, &created)
|
||||
if created.ClientSecret != "" {
|
||||
t.Fatal("expected create response to avoid exposing client_secret")
|
||||
}
|
||||
if !created.ClientSecretConfigured {
|
||||
t.Fatal("expected create response to mark client_secret as configured")
|
||||
}
|
||||
|
||||
updateResp := performSessionJSONRequest(t, engine, loginCookie, http.MethodPost, "/api/auth-sources/1/update", map[string]any{
|
||||
"name": "GitHub",
|
||||
"type": "github",
|
||||
"display_name": "GitHub",
|
||||
"is_active": true,
|
||||
"client_id": "github-client-id",
|
||||
"client_secret": "updated-secret",
|
||||
"scopes": "user:email",
|
||||
})
|
||||
|
||||
var updated model.AuthSource
|
||||
decodeResponseData(t, updateResp, &updated)
|
||||
if updated.ClientSecret != "" {
|
||||
t.Fatal("expected update response to avoid exposing client_secret")
|
||||
}
|
||||
if !updated.ClientSecretConfigured {
|
||||
t.Fatal("expected update response to mark client_secret as configured")
|
||||
}
|
||||
if !updated.IsActive {
|
||||
t.Fatal("expected auth source to be active after update")
|
||||
}
|
||||
|
||||
stored, err := model.GetAuthSourceByID(1)
|
||||
if err != nil {
|
||||
t.Fatalf("expected auth source to exist: %v", err)
|
||||
}
|
||||
if stored.ClientSecret != "updated-secret" {
|
||||
t.Fatalf("expected stored client secret to be updated, got %q", stored.ClientSecret)
|
||||
}
|
||||
|
||||
performSessionJSONRequest(t, engine, loginCookie, http.MethodPost, "/api/auth-sources/1/toggle", map[string]any{
|
||||
"is_active": false,
|
||||
})
|
||||
performSessionJSONRequest(t, engine, loginCookie, http.MethodPost, "/api/auth-sources/1/toggle", map[string]any{
|
||||
"is_active": true,
|
||||
})
|
||||
}
|
||||
|
||||
func loginAsRoot(t *testing.T, engine http.Handler) *http.Cookie {
|
||||
t.Helper()
|
||||
payload, err := json.Marshal(map[string]any{
|
||||
|
||||
@@ -41,6 +41,9 @@ func setWebRouter(router *gin.Engine, buildFS embed.FS, indexPage []byte) {
|
||||
|
||||
func serveExportedPage(c *gin.Context, buildFS fs.FS) bool {
|
||||
requestPath := strings.Trim(c.Request.URL.Path, "/")
|
||||
if isOAuthCallbackPath(requestPath) {
|
||||
requestPath = "oauth/callback"
|
||||
}
|
||||
|
||||
candidates := []string{"index.html"}
|
||||
if requestPath != "" {
|
||||
@@ -61,6 +64,19 @@ func serveExportedPage(c *gin.Context, buildFS fs.FS) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func isOAuthCallbackPath(requestPath string) bool {
|
||||
if !strings.HasPrefix(requestPath, "oauth/") || strings.Count(requestPath, "/") != 1 {
|
||||
return false
|
||||
}
|
||||
source := strings.TrimPrefix(requestPath, "oauth/")
|
||||
switch source {
|
||||
case "", "callback", "link":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeStaticExportDataNavigation() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
requestPath := c.Request.URL.Path
|
||||
|
||||
@@ -93,3 +93,20 @@ func TestCacheHeadersKeepImmutableStaticAssets(t *testing.T) {
|
||||
t.Fatalf("unexpected cache-control for static asset: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuthCallbackPathMatchesSourceNames(t *testing.T) {
|
||||
cases := map[string]bool{
|
||||
"oauth/github": true,
|
||||
"oauth/oidc-main": true,
|
||||
"oauth/1": true,
|
||||
"oauth/callback": false,
|
||||
"oauth/link": false,
|
||||
"oauth": false,
|
||||
}
|
||||
|
||||
for requestPath, expected := range cases {
|
||||
if got := isOAuthCallbackPath(requestPath); got != expected {
|
||||
t.Fatalf("expected %s match=%v, got %v", requestPath, expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user