♻️ Improve code structure and much easier to read
🐛 Fix auth middleware
			
			
This commit is contained in:
		
							
								
								
									
										95
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										95
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							| @@ -4,13 +4,40 @@ | ||||
|     <option name="autoReloadType" value="ALL" /> | ||||
|   </component> | ||||
|   <component name="ChangeListManager"> | ||||
|     <list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: Drop direct connection and uses consul"> | ||||
|       <change afterPath="$PROJECT_DIR$/pkg/hyper/auth.go" afterDir="false" /> | ||||
|       <change afterPath="$PROJECT_DIR$/pkg/hyper/auth_adaptor.go" afterDir="false" /> | ||||
|       <change afterPath="$PROJECT_DIR$/pkg/hyper/conn.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/.idea/dataSources.local.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.local.xml" afterDir="false" /> | ||||
|     <list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":technologist: Add the server side Hyper SDK"> | ||||
|       <change afterPath="$PROJECT_DIR$/pkg/internal/server/api/index.go" afterDir="false" /> | ||||
|       <change afterPath="$PROJECT_DIR$/pkg/internal/server/exts/cookies.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/gap/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/gap/server.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/hyper/auth_adaptor.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/hyper/auth_adaptor.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/embed.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/embed.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/accounts_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/accounts_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/admin/badges_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/admin/badges_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/admin/index.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/admin/index.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/auth_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/auth_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/auth_middleware.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/exts/auth.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/avatar_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/avatar_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/factors_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/factors_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/friendships_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/friendships_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/notifications_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/notifications_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/notify_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/notify_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/page_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/page_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/realm_members_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/realm_members_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/realms_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/realms_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/security_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/security_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/server.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/accounts.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/accounts.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/index.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/index.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/mfa.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/mfa.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/oauth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/oauth.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/signin.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/signin.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/ui/signup.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/ui/signup.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/userinfo_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/userinfo_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/well_known_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/well_known_api.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/ws.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/ws.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/services/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/auth.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/services/jwt.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/jwt.go" afterDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/utils/auth.go" beforeDir="false" /> | ||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/utils/request.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/exts/request.go" afterDir="false" /> | ||||
|     </list> | ||||
|     <option name="SHOW_DIALOG" value="false" /> | ||||
|     <option name="HIGHLIGHT_CONFLICTS" value="true" /> | ||||
| @@ -45,33 +72,33 @@ | ||||
|     <option name="hideEmptyMiddlePackages" value="true" /> | ||||
|     <option name="showLibraryContents" value="true" /> | ||||
|   </component> | ||||
|   <component name="PropertiesComponent">{ | ||||
|   "keyToString": { | ||||
|     "DefaultGoTemplateProperty": "Go File", | ||||
|     "Go Build.Backend.executor": "Run", | ||||
|     "Go 构建.Backend.executor": "Run", | ||||
|     "RunOnceActivity.ShowReadmeOnStart": "true", | ||||
|     "RunOnceActivity.go.formatter.settings.were.checked": "true", | ||||
|     "RunOnceActivity.go.migrated.go.modules.settings": "true", | ||||
|     "RunOnceActivity.go.modules.automatic.dependencies.download": "true", | ||||
|     "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true", | ||||
|     "git-widget-placeholder": "features/consul", | ||||
|     "go.import.settings.migrated": "true", | ||||
|     "go.sdk.automatically.set": "true", | ||||
|     "last_opened_file_path": "/Users/littlesheep", | ||||
|     "node.js.detected.package.eslint": "true", | ||||
|     "node.js.selected.package.eslint": "(autodetect)", | ||||
|     "nodejs_package_manager_path": "npm", | ||||
|     "run.code.analysis.last.selected.profile": "pProject Default", | ||||
|     "settings.editor.selected.configurable": "preferences.pluginManager", | ||||
|     "vue.rearranger.settings.migration": "true" | ||||
|   <component name="PropertiesComponent"><![CDATA[{ | ||||
|   "keyToString": { | ||||
|     "DefaultGoTemplateProperty": "Go File", | ||||
|     "Go Build.Backend.executor": "Debug", | ||||
|     "Go 构建.Backend.executor": "Run", | ||||
|     "RunOnceActivity.ShowReadmeOnStart": "true", | ||||
|     "RunOnceActivity.go.formatter.settings.were.checked": "true", | ||||
|     "RunOnceActivity.go.migrated.go.modules.settings": "true", | ||||
|     "RunOnceActivity.go.modules.automatic.dependencies.download": "true", | ||||
|     "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true", | ||||
|     "git-widget-placeholder": "master", | ||||
|     "go.import.settings.migrated": "true", | ||||
|     "go.sdk.automatically.set": "true", | ||||
|     "last_opened_file_path": "/Users/littlesheep", | ||||
|     "node.js.detected.package.eslint": "true", | ||||
|     "node.js.selected.package.eslint": "(autodetect)", | ||||
|     "nodejs_package_manager_path": "npm", | ||||
|     "run.code.analysis.last.selected.profile": "pProject Default", | ||||
|     "settings.editor.selected.configurable": "preferences.pluginManager", | ||||
|     "vue.rearranger.settings.migration": "true" | ||||
|   }, | ||||
|   "keyToStringList": { | ||||
|     "DatabaseDriversLRU": [ | ||||
|       "postgresql" | ||||
|   "keyToStringList": { | ||||
|     "DatabaseDriversLRU": [ | ||||
|       "postgresql" | ||||
|     ] | ||||
|   } | ||||
| }</component> | ||||
| }]]></component> | ||||
|   <component name="RecentsManager"> | ||||
|     <key name="CopyFile.RECENT_KEYS"> | ||||
|       <recent name="$PROJECT_DIR$/pkg/services" /> | ||||
| @@ -81,11 +108,11 @@ | ||||
|       <recent name="$PROJECT_DIR$/pkg" /> | ||||
|     </key> | ||||
|     <key name="MoveFile.RECENT_KEYS"> | ||||
|       <recent name="$PROJECT_DIR$/pkg/internal/server/exts" /> | ||||
|       <recent name="$PROJECT_DIR$/pkg/internal/server/api" /> | ||||
|       <recent name="$PROJECT_DIR$/pkg/internal" /> | ||||
|       <recent name="$PROJECT_DIR$/pkg" /> | ||||
|       <recent name="$PROJECT_DIR$/pkg/views/users/directory" /> | ||||
|       <recent name="$PROJECT_DIR$/pkg/views/users" /> | ||||
|       <recent name="$PROJECT_DIR$/pkg/utils" /> | ||||
|     </key> | ||||
|   </component> | ||||
|   <component name="RunAnythingCache"> | ||||
| @@ -147,7 +174,6 @@ | ||||
|     </option> | ||||
|   </component> | ||||
|   <component name="VcsManagerConfiguration"> | ||||
|     <MESSAGE value=":recycle: Improved the notification subscriber API" /> | ||||
|     <MESSAGE value=":sparkles: E2EE Key Exchange" /> | ||||
|     <MESSAGE value=":bug: Bug fixes on E2EE" /> | ||||
|     <MESSAGE value=":bug: Fix key exchange cause echo" /> | ||||
| @@ -172,7 +198,8 @@ | ||||
|     <MESSAGE value=":sparkles: Consul registration" /> | ||||
|     <MESSAGE value=":wastebasket: Remove HTTP provision to consul" /> | ||||
|     <MESSAGE value=":sparkles: Drop direct connection and uses consul" /> | ||||
|     <option name="LAST_COMMIT_MESSAGE" value=":sparkles: Drop direct connection and uses consul" /> | ||||
|     <MESSAGE value=":technologist: Add the server side Hyper SDK" /> | ||||
|     <option name="LAST_COMMIT_MESSAGE" value=":technologist: Add the server side Hyper SDK" /> | ||||
|   </component> | ||||
|   <component name="VgoProject"> | ||||
|     <settings-migrated>true</settings-migrated> | ||||
|   | ||||
| @@ -12,7 +12,6 @@ const CookieAtk = "__hydrogen_atk" | ||||
| const CookieRtk = "__hydrogen_rtk" | ||||
|  | ||||
| func (v *HyperConn) AuthMiddleware(c *fiber.Ctx) error { | ||||
| 	// Detect token | ||||
| 	var atk string | ||||
| 	if cookie := c.Cookies(CookieAtk); len(cookie) > 0 { | ||||
| 		atk = cookie | ||||
| @@ -42,7 +41,6 @@ func (v *HyperConn) AuthMiddleware(c *fiber.Ctx) error { | ||||
| 			}) | ||||
| 		} | ||||
| 		c.Locals("p_user", user) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return c.Next() | ||||
|   | ||||
| @@ -2,5 +2,5 @@ package pkg | ||||
|  | ||||
| import "embed" | ||||
|  | ||||
| //go:embed views/* | ||||
| //go:embed all:views/* | ||||
| var FS embed.FS | ||||
|   | ||||
| @@ -2,16 +2,16 @@ package admin | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| func grantBadge(c *fiber.Ctx) error { | ||||
| 	if err := utils.CheckPermissions(c, "AdminGrantBadges", true); err != nil { | ||||
| 	if err := exts.EnsureGrantedPerm(c, "AdminGrantBadges", true); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @@ -21,7 +21,7 @@ func grantBadge(c *fiber.Ctx) error { | ||||
| 		AccountID uint           `json:"account_id"` | ||||
| 	} | ||||
|  | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @@ -44,7 +44,7 @@ func grantBadge(c *fiber.Ctx) error { | ||||
| } | ||||
|  | ||||
| func revokeBadge(c *fiber.Ctx) error { | ||||
| 	if err := utils.CheckPermissions(c, "AdminRevokeBadges", true); err != nil { | ||||
| 	if err := exts.EnsureGrantedPerm(c, "AdminRevokeBadges", true); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -4,8 +4,8 @@ import ( | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| func MapAdminEndpoints(A *fiber.App, authMiddleware fiber.Handler) { | ||||
| 	admin := A.Group("/api/admin").Use(authMiddleware) | ||||
| func MapAdminEndpoints(A *fiber.App) { | ||||
| 	admin := A.Group("/api/admin") | ||||
| 	{ | ||||
| 		admin.Post("/badges", grantBadge) | ||||
| 		admin.Delete("/badges/:badgeId", revokeBadge) | ||||
|   | ||||
| @@ -1,12 +1,11 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 
 | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| @@ -16,7 +15,10 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| func getUserinfo(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	var data models.Account | ||||
| 	if err := database.C. | ||||
| @@ -47,7 +49,10 @@ func getUserinfo(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func getEvents(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	take := c.QueryInt("take", 0) | ||||
| 	offset := c.QueryInt("offset", 0) | ||||
| 
 | ||||
| @@ -76,7 +81,10 @@ func getEvents(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func editUserinfo(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		Nick        string    `json:"nick" validate:"required,min=4,max=24"` | ||||
| @@ -86,7 +94,7 @@ func editUserinfo(c *fiber.Ctx) error { | ||||
| 		Birthday    time.Time `json:"birthday"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -116,7 +124,10 @@ func editUserinfo(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func killSession(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	id, _ := c.ParamsInt("ticketId", 0) | ||||
| 
 | ||||
| 	if err := database.C.Delete(&models.AuthTicket{}, &models.AuthTicket{ | ||||
| @@ -138,7 +149,7 @@ func doRegister(c *fiber.Ctx) error { | ||||
| 		MagicToken string `json:"magic_token"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} else if viper.GetBool("use_registration_magic_token") && len(data.MagicToken) <= 0 { | ||||
| 		return fmt.Errorf("missing magic token in request") | ||||
| @@ -167,7 +178,7 @@ func doRegisterConfirm(c *fiber.Ctx) error { | ||||
| 		Code string `json:"code" validate:"required"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -1,8 +1,8 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| @@ -16,7 +16,7 @@ func doAuthenticate(c *fiber.Ctx) error { | ||||
| 		Password string `json:"password" validate:"required"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -48,7 +48,7 @@ func doMultiFactorAuthenticate(c *fiber.Ctx) error { | ||||
| 		Code     string `json:"code" validate:"required"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -85,7 +85,7 @@ func getToken(c *fiber.Ctx) error { | ||||
| 		GrantType    string `json:"grant_type" form:"grant_type"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -134,7 +134,7 @@ func getToken(c *fiber.Ctx) error { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, "unsupported exchange token type") | ||||
| 	} | ||||
| 
 | ||||
| 	services.SetJwtCookieSet(c, access, refresh) | ||||
| 	exts.SetAuthCookies(c, access, refresh) | ||||
| 
 | ||||
| 	return c.JSON(fiber.Map{ | ||||
| 		"id_token":      access, | ||||
| @@ -1,4 +1,4 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| @@ -7,20 +7,27 @@ import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/gap" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
| 
 | ||||
| func setAvatar(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		AttachmentID uint `json:"attachment" validate:"required"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -47,13 +54,16 @@ func setAvatar(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func setBanner(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		AttachmentID uint `json:"attachment" validate:"required"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -1,4 +1,4 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| @@ -1,14 +1,17 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| func listFriendship(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	status := c.QueryInt("status", -1) | ||||
| 
 | ||||
| 	var err error | ||||
| @@ -27,7 +30,10 @@ func listFriendship(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func getFriendship(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	relatedId, _ := c.ParamsInt("relatedId", 0) | ||||
| 
 | ||||
| 	related, err := services.GetAccount(uint(relatedId)) | ||||
| @@ -43,7 +49,10 @@ func getFriendship(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func makeFriendship(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	relatedName := c.Query("related") | ||||
| 	relatedId, _ := c.ParamsInt("relatedId", 0) | ||||
| 
 | ||||
| @@ -72,14 +81,17 @@ func makeFriendship(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func editFriendship(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	relatedId, _ := c.ParamsInt("relatedId", 0) | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		Status uint8 `json:"status"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| @@ -103,7 +115,10 @@ func editFriendship(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func deleteFriendship(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	relatedId, _ := c.ParamsInt("relatedId", 0) | ||||
| 
 | ||||
| 	related, err := services.GetAccount(uint(relatedId)) | ||||
							
								
								
									
										90
									
								
								pkg/internal/server/api/index.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								pkg/internal/server/api/index.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| package api | ||||
|  | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"github.com/gofiber/contrib/websocket" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| func MapAPIs(app *fiber.App) { | ||||
| 	app.Get("/.well-known", getMetadata) | ||||
| 	app.Get("/.well-known/openid-configuration", getOidcConfiguration) | ||||
|  | ||||
| 	api := app.Group("/api").Name("API") | ||||
| 	{ | ||||
| 		notify := api.Group("/notifications").Name("Notifications API") | ||||
| 		{ | ||||
| 			notify.Get("/", getNotifications) | ||||
| 			notify.Post("/subscribe", addNotifySubscriber) | ||||
| 			notify.Put("/batch/read", markNotificationReadBatch) | ||||
| 			notify.Put("/:notificationId/read", markNotificationRead) | ||||
| 		} | ||||
|  | ||||
| 		me := api.Group("/users/me").Name("Myself Operations") | ||||
| 		{ | ||||
|  | ||||
| 			me.Put("/avatar", setAvatar) | ||||
| 			me.Put("/banner", setBanner) | ||||
|  | ||||
| 			me.Get("/", getUserinfo) | ||||
| 			me.Get("/page", getOwnPersonalPage) | ||||
| 			me.Put("/", editUserinfo) | ||||
| 			me.Put("/page", editPersonalPage) | ||||
| 			me.Get("/events", getEvents) | ||||
| 			me.Get("/tickets", getTickets) | ||||
| 			me.Delete("/tickets/:ticketId", killSession) | ||||
|  | ||||
| 			me.Post("/confirm", doRegisterConfirm) | ||||
|  | ||||
| 			friends := me.Group("/friends").Name("Friends") | ||||
| 			{ | ||||
| 				friends.Get("/", listFriendship) | ||||
| 				friends.Get("/:relatedId", getFriendship) | ||||
| 				friends.Post("/", makeFriendship) | ||||
| 				friends.Post("/:relatedId", makeFriendship) | ||||
| 				friends.Put("/:relatedId", editFriendship) | ||||
| 				friends.Delete("/:relatedId", deleteFriendship) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		directory := api.Group("/users/:alias").Name("User Directory") | ||||
| 		{ | ||||
| 			directory.Get("/", getOtherUserinfo) | ||||
| 			directory.Get("/page", getPersonalPage) | ||||
| 		} | ||||
|  | ||||
| 		api.Post("/users", doRegister) | ||||
|  | ||||
| 		api.Post("/auth", doAuthenticate) | ||||
| 		api.Post("/auth/token", getToken) | ||||
| 		api.Post("/auth/factors/:factorId", requestFactorToken) | ||||
|  | ||||
| 		realms := api.Group("/realms").Name("Realms API") | ||||
| 		{ | ||||
| 			realms.Get("/", listCommunityRealm) | ||||
| 			realms.Get("/me", listOwnedRealm) | ||||
| 			realms.Get("/me/available", listAvailableRealm) | ||||
| 			realms.Get("/:realm", getRealm) | ||||
| 			realms.Get("/:realm/members", listRealmMembers) | ||||
| 			realms.Get("/:realm/members/me", getMyRealmMember) | ||||
| 			realms.Post("/", createRealm) | ||||
| 			realms.Put("/:realmId", editRealm) | ||||
| 			realms.Delete("/:realmId", deleteRealm) | ||||
| 			realms.Post("/:realm/members", addRealmMember) | ||||
| 			realms.Delete("/:realm/members", removeRealmMember) | ||||
| 			realms.Delete("/:realm/members/me", leaveRealm) | ||||
| 		} | ||||
|  | ||||
| 		developers := api.Group("/dev").Name("Developers API") | ||||
| 		{ | ||||
| 			developers.Post("/notify", notifyUser) | ||||
| 		} | ||||
|  | ||||
| 		api.Use(func(c *fiber.Ctx) error { | ||||
| 			if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			return c.Next() | ||||
| 		}).Get("/ws", websocket.New(listenWebsocket)) | ||||
| 	} | ||||
| } | ||||
| @@ -1,18 +1,22 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| func getNotifications(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	take := c.QueryInt("take", 0) | ||||
| 	offset := c.QueryInt("offset", 0) | ||||
| 
 | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	tx := database.C.Where(&models.Notification{RecipientID: user.ID}).Model(&models.Notification{}) | ||||
| 
 | ||||
| 	var count int64 | ||||
| @@ -36,9 +40,16 @@ func getNotifications(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func markNotificationRead(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	id, _ := c.ParamsInt("notificationId", 0) | ||||
| 
 | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	var notify models.Notification | ||||
| 	if err := database.C.Where(&models.Notification{ | ||||
| 		BaseModel:   models.BaseModel{ID: uint(id)}, | ||||
| @@ -55,13 +66,16 @@ func markNotificationRead(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func markNotificationReadBatch(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		MessageIDs []uint `json:"messages"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| @@ -75,7 +89,10 @@ func markNotificationReadBatch(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func addNotifySubscriber(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		Provider    string `json:"provider" validate:"required"` | ||||
| @@ -83,7 +100,7 @@ func addNotifySubscriber(c *fiber.Ctx) error { | ||||
| 		DeviceID    string `json:"device_id" validate:"required"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -1,9 +1,9 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| @@ -21,7 +21,7 @@ func notifyUser(c *fiber.Ctx) error { | ||||
| 		UserID       uint                      `json:"user_id" validate:"required"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -1,9 +1,9 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| @@ -28,7 +28,10 @@ func getPersonalPage(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func getOwnPersonalPage(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	var page models.AccountPage | ||||
| 	if err := database.C. | ||||
| @@ -41,14 +44,17 @@ func getOwnPersonalPage(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func editPersonalPage(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		Content string                    `json:"content"` | ||||
| 		Links   []models.AccountPageLinks `json:"links"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -1,10 +1,10 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| @@ -22,7 +22,10 @@ func listRealmMembers(c *fiber.Ctx) error { | ||||
| 
 | ||||
| func getMyRealmMember(c *fiber.Ctx) error { | ||||
| 	alias := c.Params("realm") | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	if realm, err := services.GetRealmWithAlias(alias); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||
| @@ -34,14 +37,17 @@ func getMyRealmMember(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func addRealmMember(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	alias := c.Params("realm") | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		Target string `json:"target" validate:"required"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -65,14 +71,17 @@ func addRealmMember(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func removeRealmMember(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	alias := c.Params("realm") | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		Target string `json:"target" validate:"required"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -96,7 +105,10 @@ func removeRealmMember(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func leaveRealm(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	alias := c.Params("realm") | ||||
| 
 | ||||
| 	realm, err := services.GetRealmWithAlias(alias) | ||||
| @@ -1,10 +1,10 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| @@ -27,7 +27,10 @@ func listCommunityRealm(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func listOwnedRealm(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	if realms, err := services.ListOwnedRealm(user); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||
| 	} else { | ||||
| @@ -36,7 +39,10 @@ func listOwnedRealm(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func listAvailableRealm(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	if realms, err := services.ListAvailableRealm(user); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||
| 	} else { | ||||
| @@ -45,10 +51,10 @@ func listAvailableRealm(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func createRealm(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := utils.CheckPermissions(c, "CreateRealms", true); err != nil { | ||||
| 	if err := exts.EnsureGrantedPerm(c, "CreateRealms", true); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	var data struct { | ||||
| 		Alias       string `json:"alias" validate:"required,lowercase,min=4,max=32"` | ||||
| @@ -58,7 +64,7 @@ func createRealm(c *fiber.Ctx) error { | ||||
| 		IsCommunity bool   `json:"is_community"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -78,7 +84,10 @@ func createRealm(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func editRealm(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	id, _ := c.ParamsInt("realmId", 0) | ||||
| 
 | ||||
| 	var data struct { | ||||
| @@ -89,7 +98,7 @@ func editRealm(c *fiber.Ctx) error { | ||||
| 		IsCommunity bool   `json:"is_community"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| @@ -116,7 +125,10 @@ func editRealm(c *fiber.Ctx) error { | ||||
| } | ||||
| 
 | ||||
| func deleteRealm(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	id, _ := c.ParamsInt("realmId", 0) | ||||
| 
 | ||||
| 	var realm models.Realm | ||||
| @@ -1,13 +1,17 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| func getTickets(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	take := c.QueryInt("take", 0) | ||||
| 	offset := c.QueryInt("offset", 0) | ||||
| 
 | ||||
| @@ -1,4 +1,4 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| @@ -1,4 +1,4 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| @@ -1,8 +1,7 @@ | ||||
| package server | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"github.com/gofiber/contrib/websocket" | ||||
| @@ -12,7 +11,7 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| func listenWebsocket(c *websocket.Conn) { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 
 | ||||
| 	// Push connection | ||||
| 	services.ClientRegister(user, c) | ||||
| @@ -1,55 +0,0 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| func authMiddleware(c *fiber.Ctx) error { | ||||
| 	var token string | ||||
| 	if cookie := c.Cookies(services.CookieAccessKey); len(cookie) > 0 { | ||||
| 		token = cookie | ||||
| 	} | ||||
| 	if header := c.Get(fiber.HeaderAuthorization); len(header) > 0 { | ||||
| 		tk := strings.Replace(header, "Bearer", "", 1) | ||||
| 		token = strings.TrimSpace(tk) | ||||
| 	} | ||||
| 	if query := c.Query("tk"); len(query) > 0 { | ||||
| 		token = strings.TrimSpace(query) | ||||
| 	} | ||||
|  | ||||
| 	c.Locals("token", token) | ||||
|  | ||||
| 	if err := authFunc(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return c.Next() | ||||
| } | ||||
|  | ||||
| func authFunc(c *fiber.Ctx, overrides ...string) error { | ||||
| 	var token string | ||||
| 	if len(overrides) > 0 { | ||||
| 		token = overrides[0] | ||||
| 	} else { | ||||
| 		if tk, ok := c.Locals("token").(string); !ok { | ||||
| 			return fiber.NewError(fiber.StatusUnauthorized) | ||||
| 		} else { | ||||
| 			token = tk | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	rtk := c.Cookies(services.CookieRefreshKey) | ||||
| 	if ctx, perms, atk, rtk, err := services.Authenticate(token, rtk, 0); err == nil { | ||||
| 		if atk != token { | ||||
| 			services.SetJwtCookieSet(c, atk, rtk) | ||||
| 		} | ||||
| 		c.Locals("permissions", perms) | ||||
| 		c.Locals("principal", ctx.Account) | ||||
| 		return nil | ||||
| 	} else { | ||||
| 		return err | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										53
									
								
								pkg/internal/server/exts/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								pkg/internal/server/exts/auth.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| package exts | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/hyper" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func AuthMiddleware(c *fiber.Ctx) error { | ||||
| 	var atk string | ||||
| 	if cookie := c.Cookies(hyper.CookieAtk); len(cookie) > 0 { | ||||
| 		atk = cookie | ||||
| 	} | ||||
| 	if header := c.Get(fiber.HeaderAuthorization); len(header) > 0 { | ||||
| 		tk := strings.Replace(header, "Bearer", "", 1) | ||||
| 		atk = strings.TrimSpace(tk) | ||||
| 	} | ||||
|  | ||||
| 	c.Locals("p_token", atk) | ||||
|  | ||||
| 	rtk := c.Cookies(hyper.CookieRtk) | ||||
| 	if ctx, perms, newAtk, newRtk, err := services.Authenticate(atk, rtk, 0); err == nil { | ||||
| 		if newAtk != atk { | ||||
| 			SetAuthCookies(c, newAtk, newRtk) | ||||
| 		} | ||||
| 		c.Locals("permissions", perms) | ||||
| 		c.Locals("user", ctx.Account) | ||||
| 	} | ||||
|  | ||||
| 	return c.Next() | ||||
| } | ||||
|  | ||||
| func EnsureAuthenticated(c *fiber.Ctx) error { | ||||
| 	if _, ok := c.Locals("user").(models.Account); !ok { | ||||
| 		return fiber.NewError(fiber.StatusUnauthorized) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func EnsureGrantedPerm(c *fiber.Ctx, key string, val any) error { | ||||
| 	if err := EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	perms := c.Locals("permissions").(map[string]any) | ||||
| 	if !services.HasPermNode(perms, key, val) { | ||||
| 		return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("missing permission: %s", key)) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										27
									
								
								pkg/internal/server/exts/cookies.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								pkg/internal/server/exts/cookies.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| package exts | ||||
|  | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/hyper" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| func SetAuthCookies(c *fiber.Ctx, atk, rtk string) { | ||||
| 	c.Cookie(&fiber.Cookie{ | ||||
| 		Name:     hyper.CookieAtk, | ||||
| 		Value:    atk, | ||||
| 		Domain:   viper.GetString("security.cookie_domain"), | ||||
| 		SameSite: viper.GetString("security.cookie_samesite"), | ||||
| 		Expires:  time.Now().Add(60 * time.Minute), | ||||
| 		Path:     "/", | ||||
| 	}) | ||||
| 	c.Cookie(&fiber.Cookie{ | ||||
| 		Name:     hyper.CookieRtk, | ||||
| 		Value:    rtk, | ||||
| 		Domain:   viper.GetString("security.cookie_domain"), | ||||
| 		SameSite: viper.GetString("security.cookie_samesite"), | ||||
| 		Expires:  time.Now().Add(24 * 30 * time.Hour), | ||||
| 		Path:     "/", | ||||
| 	}) | ||||
| } | ||||
| @@ -1,8 +1,6 @@ | ||||
| package utils | ||||
| package exts | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"github.com/go-playground/validator/v10" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| @@ -21,17 +19,6 @@ func BindAndValidate(c *fiber.Ctx, out any) error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func GetPermissions(c *fiber.Ctx) map[string]any { | ||||
| 	return c.Locals("permissions").(map[string]any) | ||||
| } | ||||
| 
 | ||||
| func CheckPermissions(c *fiber.Ctx, key string, val any) error { | ||||
| 	if !services.HasPermNode(GetPermissions(c), key, val) { | ||||
| 		return fiber.NewError(fiber.StatusForbidden, fmt.Sprintf("requires permission: %s = %v", key, val)) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func GetRedirectUri(c *fiber.Ctx, fallback ...string) *string { | ||||
| 	if len(c.Query("redirect_uri")) > 0 { | ||||
| 		return lo.ToPtr(c.Query("redirect_uri")) | ||||
| @@ -1,11 +1,11 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/api" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/gofiber/contrib/websocket" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/i18n" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/admin" | ||||
| @@ -61,92 +61,18 @@ func NewServer() { | ||||
| 		Output: log.Logger, | ||||
| 	})) | ||||
|  | ||||
| 	A.Use(exts.AuthMiddleware) | ||||
| 	A.Use(i18n.I18nMiddleware) | ||||
|  | ||||
| 	A.Get("/.well-known", getMetadata) | ||||
| 	A.Get("/.well-known/openid-configuration", getOidcConfiguration) | ||||
|  | ||||
| 	api := A.Group("/api").Name("API") | ||||
| 	{ | ||||
| 		notify := api.Group("/notifications").Name("Notifications API") | ||||
| 		{ | ||||
| 			notify.Get("/", authMiddleware, getNotifications) | ||||
| 			notify.Post("/subscribe", authMiddleware, addNotifySubscriber) | ||||
| 			notify.Put("/batch/read", authMiddleware, markNotificationReadBatch) | ||||
| 			notify.Put("/:notificationId/read", authMiddleware, markNotificationRead) | ||||
| 		} | ||||
|  | ||||
| 		me := api.Group("/users/me").Name("Myself Operations") | ||||
| 		{ | ||||
|  | ||||
| 			me.Put("/avatar", authMiddleware, setAvatar) | ||||
| 			me.Put("/banner", authMiddleware, setBanner) | ||||
|  | ||||
| 			me.Get("/", authMiddleware, getUserinfo) | ||||
| 			me.Get("/page", authMiddleware, getOwnPersonalPage) | ||||
| 			me.Put("/", authMiddleware, editUserinfo) | ||||
| 			me.Put("/page", authMiddleware, editPersonalPage) | ||||
| 			me.Get("/events", authMiddleware, getEvents) | ||||
| 			me.Get("/tickets", authMiddleware, getTickets) | ||||
| 			me.Delete("/tickets/:ticketId", authMiddleware, killSession) | ||||
|  | ||||
| 			me.Post("/confirm", doRegisterConfirm) | ||||
|  | ||||
| 			friends := me.Group("/friends").Name("Friends") | ||||
| 			{ | ||||
| 				friends.Get("/", authMiddleware, listFriendship) | ||||
| 				friends.Get("/:relatedId", authMiddleware, getFriendship) | ||||
| 				friends.Post("/", authMiddleware, makeFriendship) | ||||
| 				friends.Post("/:relatedId", authMiddleware, makeFriendship) | ||||
| 				friends.Put("/:relatedId", authMiddleware, editFriendship) | ||||
| 				friends.Delete("/:relatedId", authMiddleware, deleteFriendship) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		directory := api.Group("/users/:alias").Name("User Directory") | ||||
| 		{ | ||||
| 			directory.Get("/", getOtherUserinfo) | ||||
| 			directory.Get("/page", getPersonalPage) | ||||
| 		} | ||||
|  | ||||
| 		api.Post("/users", doRegister) | ||||
|  | ||||
| 		api.Post("/auth", doAuthenticate) | ||||
| 		api.Post("/auth/token", getToken) | ||||
| 		api.Post("/auth/factors/:factorId", requestFactorToken) | ||||
|  | ||||
| 		realms := api.Group("/realms").Name("Realms API") | ||||
| 		{ | ||||
| 			realms.Get("/", listCommunityRealm) | ||||
| 			realms.Get("/me", authMiddleware, listOwnedRealm) | ||||
| 			realms.Get("/me/available", authMiddleware, listAvailableRealm) | ||||
| 			realms.Get("/:realm", getRealm) | ||||
| 			realms.Get("/:realm/members", listRealmMembers) | ||||
| 			realms.Get("/:realm/members/me", authMiddleware, getMyRealmMember) | ||||
| 			realms.Post("/", authMiddleware, createRealm) | ||||
| 			realms.Put("/:realmId", authMiddleware, editRealm) | ||||
| 			realms.Delete("/:realmId", authMiddleware, deleteRealm) | ||||
| 			realms.Post("/:realm/members", authMiddleware, addRealmMember) | ||||
| 			realms.Delete("/:realm/members", authMiddleware, removeRealmMember) | ||||
| 			realms.Delete("/:realm/members/me", authMiddleware, leaveRealm) | ||||
| 		} | ||||
|  | ||||
| 		developers := api.Group("/dev").Name("Developers API") | ||||
| 		{ | ||||
| 			developers.Post("/notify", notifyUser) | ||||
| 		} | ||||
|  | ||||
| 		api.Get("/ws", authMiddleware, websocket.New(listenWebsocket)) | ||||
| 	} | ||||
|  | ||||
| 	A.Use(favicon.New(favicon.Config{ | ||||
| 		FileSystem: http.FS(pkg.FS), | ||||
| 		File:       "views/favicon.png", | ||||
| 		URL:        "/favicon.png", | ||||
| 	})) | ||||
|  | ||||
| 	admin.MapAdminEndpoints(A, authMiddleware) | ||||
| 	ui.MapUserInterface(A, authFunc) | ||||
| 	api.MapAPIs(A) | ||||
| 	admin.MapAdminEndpoints(A) | ||||
| 	ui.MapUserInterface(A) | ||||
| } | ||||
|  | ||||
| func Listen() { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package ui | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"html/template" | ||||
| 	"time" | ||||
|  | ||||
| @@ -15,7 +16,10 @@ import ( | ||||
| ) | ||||
|  | ||||
| func selfUserinfoPage(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return DoAuthRedirect(c) | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
|  | ||||
| 	var data models.Account | ||||
| 	if err := database.C. | ||||
|   | ||||
| @@ -3,28 +3,15 @@ package ui | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| func MapUserInterface(A *fiber.App, authFunc utils.AuthFunc) { | ||||
| 	authCheckWare := func(c *fiber.Ctx) error { | ||||
| 		var token string | ||||
| 		if cookie := c.Cookies(services.CookieAccessKey); len(cookie) > 0 { | ||||
| 			token = cookie | ||||
| 		} | ||||
|  | ||||
| 		c.Locals("token", token) | ||||
|  | ||||
| 		if err := authFunc(c); err != nil { | ||||
| 			uri := c.Request().URI().FullURI() | ||||
| 			return c.Redirect(fmt.Sprintf("/sign-in?redirect_uri=%s", string(uri))) | ||||
| 		} else { | ||||
| 			return c.Next() | ||||
| 		} | ||||
| 	} | ||||
| func DoAuthRedirect(c *fiber.Ctx) error { | ||||
| 	uri := c.Request().URI().FullURI() | ||||
| 	return c.Redirect(fmt.Sprintf("/sign-in?redirect_uri=%s", string(uri))) | ||||
| } | ||||
|  | ||||
| func MapUserInterface(A *fiber.App) { | ||||
| 	pages := A.Group("/").Name("Pages") | ||||
|  | ||||
| 	pages.Get("/", func(c *fiber.Ctx) error { | ||||
| @@ -35,13 +22,13 @@ func MapUserInterface(A *fiber.App, authFunc utils.AuthFunc) { | ||||
| 	pages.Get("/sign-in", signinPage) | ||||
| 	pages.Get("/mfa", mfaRequestPage) | ||||
| 	pages.Get("/mfa/apply", mfaApplyPage) | ||||
| 	pages.Get("/authorize", authCheckWare, authorizePage) | ||||
| 	pages.Get("/authorize", authorizePage) | ||||
|  | ||||
| 	pages.Post("/sign-up", signupAction) | ||||
| 	pages.Post("/sign-in", signinAction) | ||||
| 	pages.Post("/mfa", mfaRequestAction) | ||||
| 	pages.Post("/mfa/apply", mfaApplyAction) | ||||
| 	pages.Post("/authorize", authCheckWare, authorizeAction) | ||||
| 	pages.Post("/authorize", authorizeAction) | ||||
|  | ||||
| 	pages.Get("/users/me", authCheckWare, selfUserinfoPage) | ||||
| 	pages.Get("/users/me", selfUserinfoPage) | ||||
| } | ||||
|   | ||||
| @@ -3,8 +3,8 @@ package ui | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/nicksnyder/go-i18n/v2/i18n" | ||||
| 	"github.com/samber/lo" | ||||
| @@ -68,7 +68,7 @@ func mfaRequestAction(c *fiber.Ctx) error { | ||||
| 	} | ||||
|  | ||||
| 	redirectBackUri := "/sign-in" | ||||
| 	err := utils.BindAndValidate(c, &data) | ||||
| 	err := exts.BindAndValidate(c, &data) | ||||
|  | ||||
| 	if data.TicketID > 0 { | ||||
| 		redirectBackUri = fmt.Sprintf("/mfa?ticket=%d", data.TicketID) | ||||
| @@ -95,7 +95,7 @@ func mfaRequestAction(c *fiber.Ctx) error { | ||||
| 	} | ||||
|  | ||||
| 	return flash.WithData(c, fiber.Map{ | ||||
| 		"redirect_uri": utils.GetRedirectUri(c), | ||||
| 		"redirect_uri": exts.GetRedirectUri(c), | ||||
| 	}).Redirect(fmt.Sprintf("/mfa/apply?ticket=%d&factor=%d", data.TicketID, factor.ID)) | ||||
| } | ||||
|  | ||||
| @@ -145,7 +145,7 @@ func mfaApplyAction(c *fiber.Ctx) error { | ||||
| 	} | ||||
|  | ||||
| 	redirectBackUri := "/sign-in" | ||||
| 	err := utils.BindAndValidate(c, &data) | ||||
| 	err := exts.BindAndValidate(c, &data) | ||||
|  | ||||
| 	if data.TicketID > 0 { | ||||
| 		redirectBackUri = fmt.Sprintf("/mfa/apply?ticket=%d&factor=%d", data.TicketID, data.FactorID) | ||||
| @@ -187,8 +187,8 @@ func mfaApplyAction(c *fiber.Ctx) error { | ||||
| 			"message": fmt.Sprintf("failed to exchange token: %v", err.Error()), | ||||
| 		}).Redirect("/sign-in") | ||||
| 	} else { | ||||
| 		services.SetJwtCookieSet(c, access, refresh) | ||||
| 		exts.SetAuthCookies(c, access, refresh) | ||||
| 	} | ||||
|  | ||||
| 	return c.Redirect(lo.FromPtr(utils.GetRedirectUri(c, "/users/me"))) | ||||
| 	return c.Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/users/me"))) | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/nicksnyder/go-i18n/v2/i18n" | ||||
| @@ -16,7 +17,11 @@ import ( | ||||
|  | ||||
| func authorizePage(c *fiber.Ctx) error { | ||||
| 	localizer := c.Locals("localizer").(*i18n.Localizer) | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
|  | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return DoAuthRedirect(c) | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
|  | ||||
| 	id := c.Query("client_id") | ||||
| 	redirect := c.Query("redirect_uri") | ||||
| @@ -81,12 +86,19 @@ func authorizePage(c *fiber.Ctx) error { | ||||
| } | ||||
|  | ||||
| func authorizeAction(c *fiber.Ctx) error { | ||||
| 	user := c.Locals("principal").(models.Account) | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	id := c.Query("client_id") | ||||
| 	response := c.Query("response_type") | ||||
| 	redirect := c.Query("redirect_uri") | ||||
| 	scope := c.Query("scope") | ||||
|  | ||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { | ||||
| 		return DoAuthRedirect(c) | ||||
| 	} | ||||
|  | ||||
| 	redirectBackUri := "/authorize?" + string(c.Request().URI().QueryString()) | ||||
|  | ||||
| 	if len(scope) <= 0 { | ||||
|   | ||||
| @@ -2,8 +2,8 @@ package ui | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/nicksnyder/go-i18n/v2/i18n" | ||||
| 	"github.com/samber/lo" | ||||
| @@ -47,7 +47,7 @@ func signinAction(c *fiber.Ctx) error { | ||||
| 		Password string `form:"password" validate:"required"` | ||||
| 	} | ||||
|  | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return flash.WithInfo(c, fiber.Map{ | ||||
| 			"message": err.Error(), | ||||
| 		}).Redirect("/sign-in") | ||||
| @@ -76,7 +76,7 @@ func signinAction(c *fiber.Ctx) error { | ||||
|  | ||||
| 	if ticket.IsAvailable() != nil { | ||||
| 		return flash.WithData(c, fiber.Map{ | ||||
| 			"redirect_uri": utils.GetRedirectUri(c), | ||||
| 			"redirect_uri": exts.GetRedirectUri(c), | ||||
| 		}).Redirect(fmt.Sprintf("/mfa?ticket=%d", ticket.ID)) | ||||
| 	} | ||||
|  | ||||
| @@ -86,8 +86,8 @@ func signinAction(c *fiber.Ctx) error { | ||||
| 			"message": fmt.Sprintf("failed to exchange token: %v", err.Error()), | ||||
| 		}).Redirect("/sign-in") | ||||
| 	} else { | ||||
| 		services.SetJwtCookieSet(c, access, refresh) | ||||
| 		exts.SetAuthCookies(c, access, refresh) | ||||
| 	} | ||||
|  | ||||
| 	return c.Redirect(lo.FromPtr(utils.GetRedirectUri(c, "/users/me"))) | ||||
| 	return c.Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/users/me"))) | ||||
| } | ||||
|   | ||||
| @@ -4,8 +4,8 @@ import ( | ||||
| 	"fmt" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/services" | ||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/utils" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/nicksnyder/go-i18n/v2/i18n" | ||||
| 	"github.com/samber/lo" | ||||
| @@ -52,7 +52,7 @@ func signupAction(c *fiber.Ctx) error { | ||||
| 		MagicToken string `form:"magic_token"` | ||||
| 	} | ||||
|  | ||||
| 	if err := utils.BindAndValidate(c, &data); err != nil { | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return flash.WithInfo(c, fiber.Map{ | ||||
| 			"message": err.Error(), | ||||
| 		}).Redirect("/sign-up") | ||||
| @@ -82,6 +82,6 @@ func signupAction(c *fiber.Ctx) error { | ||||
| 	} else { | ||||
| 		return flash.WithInfo(c, fiber.Map{ | ||||
| 			"message": "account has been created. now you can sign in!", | ||||
| 		}).Redirect(lo.FromPtr(utils.GetRedirectUri(c, "/sign-in"))) | ||||
| 		}).Redirect(lo.FromPtr(exts.GetRedirectUri(c, "/sign-in"))) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -17,23 +17,23 @@ var ( | ||||
| 	authContextCache = make(map[string]models.AuthContext) | ||||
| ) | ||||
|  | ||||
| func Authenticate(access, refresh string, depth int) (ctx models.AuthContext, perms map[string]any, newAccess, newRefresh string, err error) { | ||||
| func Authenticate(atk, rtk string, rty int) (ctx models.AuthContext, perms map[string]any, newAtk, newRtk string, err error) { | ||||
| 	var claims PayloadClaims | ||||
| 	claims, err = DecodeJwt(access) | ||||
| 	claims, err = DecodeJwt(atk) | ||||
| 	if err != nil { | ||||
| 		if len(refresh) > 0 && depth < 1 { | ||||
| 		if len(rtk) > 0 && rty < 1 { | ||||
| 			// Auto refresh and retry | ||||
| 			newAccess, newRefresh, err = RefreshToken(refresh) | ||||
| 			newAtk, newRtk, err = RefreshToken(rtk) | ||||
| 			if err == nil { | ||||
| 				return Authenticate(newAccess, newRefresh, depth+1) | ||||
| 				return Authenticate(newAtk, newRtk, rty+1) | ||||
| 			} | ||||
| 		} | ||||
| 		err = fiber.NewError(fiber.StatusUnauthorized, fmt.Sprintf("invalid auth key: %v", err)) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	newAccess = access | ||||
| 	newRefresh = refresh | ||||
| 	newAtk = atk | ||||
| 	newRtk = rtk | ||||
|  | ||||
| 	if ctx, err = GetAuthContext(claims.ID); err == nil { | ||||
| 		var heldPerms map[string]any | ||||
|   | ||||
| @@ -2,16 +2,12 @@ package services | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/golang-jwt/jwt/v5" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
|  | ||||
| var CookieAccessKey = "passport_auth_key" | ||||
| var CookieRefreshKey = "passport_refresh_key" | ||||
|  | ||||
| type PayloadClaims struct { | ||||
| 	jwt.RegisteredClaims | ||||
|  | ||||
| @@ -60,22 +56,3 @@ func DecodeJwt(str string) (PayloadClaims, error) { | ||||
| 		return claims, fmt.Errorf("unexpected token payload: not payload claims type") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func SetJwtCookieSet(c *fiber.Ctx, access, refresh string) { | ||||
| 	c.Cookie(&fiber.Cookie{ | ||||
| 		Name:     CookieAccessKey, | ||||
| 		Value:    access, | ||||
| 		Domain:   viper.GetString("security.cookie_domain"), | ||||
| 		SameSite: viper.GetString("security.cookie_samesite"), | ||||
| 		Expires:  time.Now().Add(60 * time.Minute), | ||||
| 		Path:     "/", | ||||
| 	}) | ||||
| 	c.Cookie(&fiber.Cookie{ | ||||
| 		Name:     CookieRefreshKey, | ||||
| 		Value:    refresh, | ||||
| 		Domain:   viper.GetString("security.cookie_domain"), | ||||
| 		SameSite: viper.GetString("security.cookie_samesite"), | ||||
| 		Expires:  time.Now().Add(24 * 30 * time.Hour), | ||||
| 		Path:     "/", | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -1,5 +0,0 @@ | ||||
| package utils | ||||
|  | ||||
| import "github.com/gofiber/fiber/v2" | ||||
|  | ||||
| type AuthFunc func(c *fiber.Ctx, overrides ...string) error | ||||
		Reference in New Issue
	
	Block a user