Compare commits

..

No commits in common. "1e2d5e9f9d87b8ba27cf7fd96b0094341454ba06" and "0e89e325d480190c137d60dac2fb6db86d16c198" have entirely different histories.

25 changed files with 376 additions and 668 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,2 @@
#n:public #n:public
!<md> [7186, 0, null, null, -2147483648, -2147483648] !<md> [6992, 0, null, null, -2147483648, -2147483648]

67
.idea/workspace.xml generated
View File

@ -4,14 +4,9 @@
<option name="autoReloadType" value="ALL" /> <option name="autoReloadType" value="ALL" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: Bug fixes of permission check"> <list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":bug: Fix key exchange cause echo">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/grpc/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/grpc/auth.go" afterDir="false" /> <change beforePath="$PROJECT_DIR$/pkg/services/notifications.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/services/notifications.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/grpc/proto/auth.pb.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/grpc/proto/auth.pb.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/grpc/proto/auth.proto" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/grpc/proto/auth.proto" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/grpc/proto/auth_grpc.pb.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/grpc/proto/auth_grpc.pb.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/server/auth_middleware.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/server/auth_middleware.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/services/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/services/auth.go" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -46,32 +41,32 @@
<option name="hideEmptyMiddlePackages" value="true" /> <option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent"><![CDATA[{ <component name="PropertiesComponent">{
"keyToString": { &quot;keyToString&quot;: {
"DefaultGoTemplateProperty": "Go File", &quot;DefaultGoTemplateProperty&quot;: &quot;Go File&quot;,
"Go 构建.Backend.executor": "Run", &quot;Go 构建.Backend.executor&quot;: &quot;Run&quot;,
"RunOnceActivity.ShowReadmeOnStart": "true", &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
"RunOnceActivity.go.formatter.settings.were.checked": "true", &quot;RunOnceActivity.go.formatter.settings.were.checked&quot;: &quot;true&quot;,
"RunOnceActivity.go.migrated.go.modules.settings": "true", &quot;RunOnceActivity.go.migrated.go.modules.settings&quot;: &quot;true&quot;,
"RunOnceActivity.go.modules.automatic.dependencies.download": "true", &quot;RunOnceActivity.go.modules.automatic.dependencies.download&quot;: &quot;true&quot;,
"RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true", &quot;RunOnceActivity.go.modules.go.list.on.any.changes.was.set&quot;: &quot;true&quot;,
"git-widget-placeholder": "master", &quot;git-widget-placeholder&quot;: &quot;master&quot;,
"go.import.settings.migrated": "true", &quot;go.import.settings.migrated&quot;: &quot;true&quot;,
"go.sdk.automatically.set": "true", &quot;go.sdk.automatically.set&quot;: &quot;true&quot;,
"last_opened_file_path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/pkg/server/ui", &quot;last_opened_file_path&quot;: &quot;/Users/littlesheep/Documents/Projects/Hydrogen/Passport/pkg/server/ui&quot;,
"node.js.detected.package.eslint": "true", &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
"node.js.selected.package.eslint": "(autodetect)", &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
"nodejs_package_manager_path": "npm", &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
"run.code.analysis.last.selected.profile": "pProject Default", &quot;run.code.analysis.last.selected.profile&quot;: &quot;pProject Default&quot;,
"settings.editor.selected.configurable": "preferences.lookFeel", &quot;settings.editor.selected.configurable&quot;: &quot;preferences.lookFeel&quot;,
"vue.rearranger.settings.migration": "true" &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}, },
"keyToStringList": { &quot;keyToStringList&quot;: {
"DatabaseDriversLRU": [ &quot;DatabaseDriversLRU&quot;: [
"postgresql" &quot;postgresql&quot;
] ]
} }
}]]></component> }</component>
<component name="RecentsManager"> <component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/pkg/server/ui" /> <recent name="$PROJECT_DIR$/pkg/server/ui" />
@ -144,6 +139,11 @@
</option> </option>
</component> </component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value=":sparkles: Sign up &amp; Sign in" />
<MESSAGE value=":sparkles: An entire complete sign in user flow" />
<MESSAGE value=":sparkles: User center page" />
<MESSAGE value=":sparkles: Personalize" />
<MESSAGE value=":sparkles: OAuth" />
<MESSAGE value=":truck: Update well known" /> <MESSAGE value=":truck: Update well known" />
<MESSAGE value=":sparkles: Others userinfo" /> <MESSAGE value=":sparkles: Others userinfo" />
<MESSAGE value=":lipstick: Fix ui design" /> <MESSAGE value=":lipstick: Fix ui design" />
@ -164,12 +164,7 @@
<MESSAGE value=":sparkles: E2EE Key Exchange" /> <MESSAGE value=":sparkles: E2EE Key Exchange" />
<MESSAGE value=":bug: Bug fixes on E2EE" /> <MESSAGE value=":bug: Bug fixes on E2EE" />
<MESSAGE value=":bug: Fix key exchange cause echo" /> <MESSAGE value=":bug: Fix key exchange cause echo" />
<MESSAGE value=":bug: Fix notification push issue" /> <option name="LAST_COMMIT_MESSAGE" value=":bug: Fix key exchange cause echo" />
<MESSAGE value=":sparkles: Basis perm nodes feature" />
<MESSAGE value=":sparkles: Permission check" />
<MESSAGE value=":zap: In memory auth context cache" />
<MESSAGE value=":sparkles: Bug fixes of permission check" />
<option name="LAST_COMMIT_MESSAGE" value=":sparkles: Bug fixes of permission check" />
</component> </component>
<component name="VgoProject"> <component name="VgoProject">
<settings-migrated>true</settings-migrated> <settings-migrated>true</settings-migrated>

9
go.mod
View File

@ -9,18 +9,15 @@ require (
github.com/gofiber/fiber/v2 v2.52.4 github.com/gofiber/fiber/v2 v2.52.4
github.com/gofiber/template/html/v2 v2.1.1 github.com/gofiber/template/html/v2 v2.1.1
github.com/golang-jwt/jwt/v5 v5.2.0 github.com/golang-jwt/jwt/v5 v5.2.0
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12
github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/rs/zerolog v1.31.0 github.com/rs/zerolog v1.31.0
github.com/samber/lo v1.39.0 github.com/samber/lo v1.39.0
github.com/spf13/viper v1.18.1 github.com/spf13/viper v1.18.1
github.com/sujit-baniya/flash v0.1.8 go.etcd.io/bbolt v1.3.9
golang.org/x/crypto v0.21.0 golang.org/x/crypto v0.21.0
golang.org/x/text v0.14.0
google.golang.org/api v0.153.0 google.golang.org/api v0.153.0
google.golang.org/grpc v1.59.0 google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0 google.golang.org/protobuf v1.31.0
@ -48,6 +45,7 @@ require (
github.com/gofiber/utils v1.1.0 // indirect github.com/gofiber/utils v1.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 // indirect
github.com/google/s2a-go v0.1.7 // indirect github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect
@ -67,6 +65,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nicksnyder/go-i18n/v2 v2.4.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/philhofer/fwd v1.1.2 // indirect github.com/philhofer/fwd v1.1.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
@ -78,6 +77,7 @@ require (
github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/sujit-baniya/flash v0.1.8 // indirect
github.com/tinylib/msgp v1.1.8 // indirect github.com/tinylib/msgp v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.52.0 // indirect github.com/valyala/fasthttp v1.52.0 // indirect
@ -89,6 +89,7 @@ require (
golang.org/x/oauth2 v0.15.0 // indirect golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sync v0.5.0 // indirect golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.19.0 // indirect golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect

10
go.sum
View File

@ -16,8 +16,6 @@ cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYE
firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4=
firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
@ -131,6 +129,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -224,6 +224,8 @@ github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHY
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@ -282,6 +284,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -349,8 +353,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -44,6 +44,9 @@ func main() {
} else if err := database.RunMigration(database.C); err != nil { } else if err := database.RunMigration(database.C); err != nil {
log.Fatal().Err(err).Msg("An error occurred when running database auto migration.") log.Fatal().Err(err).Msg("An error occurred when running database auto migration.")
} }
if err := database.NewBolt(); err != nil {
log.Fatal().Err(err).Msg("An error occurred when init bolt db.")
}
// External // External
// All the things are optional so when error occurred the server won't crash // All the things are optional so when error occurred the server won't crash
@ -67,8 +70,8 @@ func main() {
// Configure timed tasks // Configure timed tasks
quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger))) quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger)))
quartz.AddFunc("@every 60m", services.DoAutoSignoff) quartz.AddFunc("@every 60m", services.DoAutoSignoff)
quartz.AddFunc("@every 60m", services.DoAutoAuthCleanup)
quartz.AddFunc("@every 60m", services.DoAutoDatabaseCleanup) quartz.AddFunc("@every 60m", services.DoAutoDatabaseCleanup)
quartz.AddFunc("@every 60s", services.RecycleAuthContext)
quartz.AddFunc("@every 5m", services.KexCleanup) quartz.AddFunc("@every 5m", services.KexCleanup)
quartz.Start() quartz.Start()
@ -82,4 +85,6 @@ func main() {
log.Info().Msgf("Passport v%s is quitting...", pkg.AppVersion) log.Info().Msgf("Passport v%s is quitting...", pkg.AppVersion)
quartz.Stop() quartz.Stop()
database.B.Close()
} }

View File

@ -4,6 +4,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/samber/lo" "github.com/samber/lo"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.etcd.io/bbolt"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
@ -26,3 +27,14 @@ func NewGorm() error {
return err return err
} }
var B *bbolt.DB
func NewBolt() error {
var err error
dsn := viper.GetString("database.bolt")
B, err = bbolt.Open(dsn, 0600, nil)
return err
}

View File

@ -3,28 +3,23 @@ package grpc
import ( import (
"context" "context"
"fmt" "fmt"
"git.solsynth.dev/hydrogen/passport/pkg/grpc/proto" "git.solsynth.dev/hydrogen/passport/pkg/grpc/proto"
"git.solsynth.dev/hydrogen/passport/pkg/services" "git.solsynth.dev/hydrogen/passport/pkg/services"
jsoniter "github.com/json-iterator/go"
"github.com/samber/lo"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
func (v *Server) Authenticate(_ context.Context, in *proto.AuthRequest) (*proto.AuthReply, error) { func (v *Server) Authenticate(_ context.Context, in *proto.AuthRequest) (*proto.AuthReply, error) {
ctx, perms, atk, rtk, err := services.Authenticate(in.GetAccessToken(), in.GetRefreshToken(), 0) user, atk, rtk, err := services.Authenticate(in.GetAccessToken(), in.GetRefreshToken(), 0)
if err != nil { if err != nil {
return &proto.AuthReply{ return &proto.AuthReply{
IsValid: false, IsValid: false,
}, nil }, nil
} else { } else {
user := ctx.Account
rawPerms, _ := jsoniter.Marshal(perms)
return &proto.AuthReply{ return &proto.AuthReply{
IsValid: true, IsValid: true,
AccessToken: &atk, AccessToken: &atk,
RefreshToken: &rtk, RefreshToken: &rtk,
Permissions: rawPerms,
TicketId: lo.ToPtr(uint64(ctx.Ticket.ID)),
Userinfo: &proto.Userinfo{ Userinfo: &proto.Userinfo{
Id: uint64(user.ID), Id: uint64(user.ID),
Name: user.Name, Name: user.Name,
@ -37,23 +32,3 @@ func (v *Server) Authenticate(_ context.Context, in *proto.AuthRequest) (*proto.
}, nil }, nil
} }
} }
func (v *Server) CheckPerm(_ context.Context, in *proto.CheckPermRequest) (*proto.CheckPermReply, error) {
claims, err := services.DecodeJwt(in.GetToken())
if err != nil {
return nil, err
}
ctx, err := services.GetAuthContext(claims.ID)
if err != nil {
return nil, err
}
var value any
_ = jsoniter.Unmarshal(in.GetValue(), &value)
perms := services.FilterPermNodes(ctx.Account.PermNodes, ctx.Ticket.Claims)
valid := services.HasPermNode(perms, in.GetKey(), value)
return &proto.CheckPermReply{
IsValid: valid,
}, nil
}

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.33.0 // protoc-gen-go v1.33.0
// protoc v5.26.1 // protoc v4.25.3
// source: auth.proto // source: auth.proto
package proto package proto
@ -179,8 +179,6 @@ type AuthReply struct {
AccessToken *string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3,oneof" json:"access_token,omitempty"` AccessToken *string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3,oneof" json:"access_token,omitempty"`
RefreshToken *string `protobuf:"bytes,3,opt,name=refresh_token,json=refreshToken,proto3,oneof" json:"refresh_token,omitempty"` RefreshToken *string `protobuf:"bytes,3,opt,name=refresh_token,json=refreshToken,proto3,oneof" json:"refresh_token,omitempty"`
Userinfo *Userinfo `protobuf:"bytes,4,opt,name=userinfo,proto3,oneof" json:"userinfo,omitempty"` Userinfo *Userinfo `protobuf:"bytes,4,opt,name=userinfo,proto3,oneof" json:"userinfo,omitempty"`
Permissions []byte `protobuf:"bytes,5,opt,name=permissions,proto3,oneof" json:"permissions,omitempty"`
TicketId *uint64 `protobuf:"varint,6,opt,name=ticket_id,json=ticketId,proto3,oneof" json:"ticket_id,omitempty"`
} }
func (x *AuthReply) Reset() { func (x *AuthReply) Reset() {
@ -243,130 +241,6 @@ func (x *AuthReply) GetUserinfo() *Userinfo {
return nil return nil
} }
func (x *AuthReply) GetPermissions() []byte {
if x != nil {
return x.Permissions
}
return nil
}
func (x *AuthReply) GetTicketId() uint64 {
if x != nil && x.TicketId != nil {
return *x.TicketId
}
return 0
}
type CheckPermRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
Value []byte `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *CheckPermRequest) Reset() {
*x = CheckPermRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CheckPermRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CheckPermRequest) ProtoMessage() {}
func (x *CheckPermRequest) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CheckPermRequest.ProtoReflect.Descriptor instead.
func (*CheckPermRequest) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{3}
}
func (x *CheckPermRequest) GetToken() string {
if x != nil {
return x.Token
}
return ""
}
func (x *CheckPermRequest) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *CheckPermRequest) GetValue() []byte {
if x != nil {
return x.Value
}
return nil
}
type CheckPermReply struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
IsValid bool `protobuf:"varint,1,opt,name=is_valid,json=isValid,proto3" json:"is_valid,omitempty"`
}
func (x *CheckPermReply) Reset() {
*x = CheckPermReply{}
if protoimpl.UnsafeEnabled {
mi := &file_auth_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CheckPermReply) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CheckPermReply) ProtoMessage() {}
func (x *CheckPermReply) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CheckPermReply.ProtoReflect.Descriptor instead.
func (*CheckPermReply) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{4}
}
func (x *CheckPermReply) GetIsValid() bool {
if x != nil {
return x.IsValid
}
return false
}
var File_auth_proto protoreflect.FileDescriptor var File_auth_proto protoreflect.FileDescriptor
var file_auth_proto_rawDesc = []byte{ var file_auth_proto_rawDesc = []byte{
@ -390,7 +264,7 @@ var file_auth_proto_rawDesc = []byte{
0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
0x52, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x88, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x88, 0x01,
0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f,
0x6b, 0x65, 0x6e, 0x22, 0xc1, 0x02, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x6b, 0x65, 0x6e, 0x22, 0xda, 0x01, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x70, 0x6c,
0x79, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x0c, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x0c,
0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01,
@ -401,33 +275,15 @@ var file_auth_proto_rawDesc = []byte{
0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66,
0x6f, 0x48, 0x02, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x88, 0x01, 0x01, 0x6f, 0x48, 0x02, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x88, 0x01, 0x01,
0x12, 0x25, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65,
0x05, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f,
0x69, 0x6f, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x6b, 0x65, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x48, 0x04, 0x52, 0x08, 0x74, 0x69, 0x32, 0x3e, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x36, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68,
0x63, 0x6b, 0x65, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70,
0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00,
0x5f, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x65, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x69, 0x74, 0x6f, 0x33,
0x63, 0x6b, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x50, 0x0a, 0x10, 0x43, 0x68, 0x65, 0x63, 0x6b,
0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2b, 0x0a, 0x0e, 0x43, 0x68, 0x65,
0x63, 0x6b, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x69,
0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69,
0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x32, 0x7d, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x36,
0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x12,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52,
0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x09, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50,
0x65, 0x72, 0x6d, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x68, 0x65, 0x63,
0x6b, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x65, 0x72, 0x6d, 0x52, 0x65,
0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -442,22 +298,18 @@ func file_auth_proto_rawDescGZIP() []byte {
return file_auth_proto_rawDescData return file_auth_proto_rawDescData
} }
var file_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_auth_proto_goTypes = []interface{}{ var file_auth_proto_goTypes = []interface{}{
(*Userinfo)(nil), // 0: proto.Userinfo (*Userinfo)(nil), // 0: proto.Userinfo
(*AuthRequest)(nil), // 1: proto.AuthRequest (*AuthRequest)(nil), // 1: proto.AuthRequest
(*AuthReply)(nil), // 2: proto.AuthReply (*AuthReply)(nil), // 2: proto.AuthReply
(*CheckPermRequest)(nil), // 3: proto.CheckPermRequest
(*CheckPermReply)(nil), // 4: proto.CheckPermReply
} }
var file_auth_proto_depIdxs = []int32{ var file_auth_proto_depIdxs = []int32{
0, // 0: proto.AuthReply.userinfo:type_name -> proto.Userinfo 0, // 0: proto.AuthReply.userinfo:type_name -> proto.Userinfo
1, // 1: proto.Auth.Authenticate:input_type -> proto.AuthRequest 1, // 1: proto.Auth.Authenticate:input_type -> proto.AuthRequest
3, // 2: proto.Auth.CheckPerm:input_type -> proto.CheckPermRequest 2, // 2: proto.Auth.Authenticate:output_type -> proto.AuthReply
2, // 3: proto.Auth.Authenticate:output_type -> proto.AuthReply 2, // [2:3] is the sub-list for method output_type
4, // 4: proto.Auth.CheckPerm:output_type -> proto.CheckPermReply 1, // [1:2] is the sub-list for method input_type
3, // [3:5] is the sub-list for method output_type
1, // [1:3] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee 1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name 0, // [0:1] is the sub-list for field type_name
@ -505,30 +357,6 @@ func file_auth_proto_init() {
return nil return nil
} }
} }
file_auth_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CheckPermRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_auth_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CheckPermReply); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
} }
file_auth_proto_msgTypes[0].OneofWrappers = []interface{}{} file_auth_proto_msgTypes[0].OneofWrappers = []interface{}{}
file_auth_proto_msgTypes[1].OneofWrappers = []interface{}{} file_auth_proto_msgTypes[1].OneofWrappers = []interface{}{}
@ -539,7 +367,7 @@ func file_auth_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_auth_proto_rawDesc, RawDescriptor: file_auth_proto_rawDesc,
NumEnums: 0, NumEnums: 0,
NumMessages: 5, NumMessages: 3,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -6,7 +6,6 @@ package proto;
service Auth { service Auth {
rpc Authenticate(AuthRequest) returns (AuthReply) {} rpc Authenticate(AuthRequest) returns (AuthReply) {}
rpc CheckPerm(CheckPermRequest) returns (CheckPermReply) {}
} }
message Userinfo { message Userinfo {
@ -29,16 +28,4 @@ message AuthReply {
optional string access_token = 2; optional string access_token = 2;
optional string refresh_token = 3; optional string refresh_token = 3;
optional Userinfo userinfo = 4; optional Userinfo userinfo = 4;
optional bytes permissions = 5;
optional uint64 ticket_id = 6;
}
message CheckPermRequest {
string token = 1;
string key = 2;
bytes value = 3;
}
message CheckPermReply {
bool is_valid = 1;
} }

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.3.0 // - protoc-gen-go-grpc v1.3.0
// - protoc v5.26.1 // - protoc v4.25.3
// source: auth.proto // source: auth.proto
package proto package proto
@ -20,7 +20,6 @@ const _ = grpc.SupportPackageIsVersion7
const ( const (
Auth_Authenticate_FullMethodName = "/proto.Auth/Authenticate" Auth_Authenticate_FullMethodName = "/proto.Auth/Authenticate"
Auth_CheckPerm_FullMethodName = "/proto.Auth/CheckPerm"
) )
// AuthClient is the client API for Auth service. // AuthClient is the client API for Auth service.
@ -28,7 +27,6 @@ const (
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type AuthClient interface { type AuthClient interface {
Authenticate(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthReply, error) Authenticate(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthReply, error)
CheckPerm(ctx context.Context, in *CheckPermRequest, opts ...grpc.CallOption) (*CheckPermReply, error)
} }
type authClient struct { type authClient struct {
@ -48,21 +46,11 @@ func (c *authClient) Authenticate(ctx context.Context, in *AuthRequest, opts ...
return out, nil return out, nil
} }
func (c *authClient) CheckPerm(ctx context.Context, in *CheckPermRequest, opts ...grpc.CallOption) (*CheckPermReply, error) {
out := new(CheckPermReply)
err := c.cc.Invoke(ctx, Auth_CheckPerm_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// AuthServer is the server API for Auth service. // AuthServer is the server API for Auth service.
// All implementations must embed UnimplementedAuthServer // All implementations must embed UnimplementedAuthServer
// for forward compatibility // for forward compatibility
type AuthServer interface { type AuthServer interface {
Authenticate(context.Context, *AuthRequest) (*AuthReply, error) Authenticate(context.Context, *AuthRequest) (*AuthReply, error)
CheckPerm(context.Context, *CheckPermRequest) (*CheckPermReply, error)
mustEmbedUnimplementedAuthServer() mustEmbedUnimplementedAuthServer()
} }
@ -73,9 +61,6 @@ type UnimplementedAuthServer struct {
func (UnimplementedAuthServer) Authenticate(context.Context, *AuthRequest) (*AuthReply, error) { func (UnimplementedAuthServer) Authenticate(context.Context, *AuthRequest) (*AuthReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented") return nil, status.Errorf(codes.Unimplemented, "method Authenticate not implemented")
} }
func (UnimplementedAuthServer) CheckPerm(context.Context, *CheckPermRequest) (*CheckPermReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method CheckPerm not implemented")
}
func (UnimplementedAuthServer) mustEmbedUnimplementedAuthServer() {} func (UnimplementedAuthServer) mustEmbedUnimplementedAuthServer() {}
// UnsafeAuthServer may be embedded to opt out of forward compatibility for this service. // UnsafeAuthServer may be embedded to opt out of forward compatibility for this service.
@ -107,24 +92,6 @@ func _Auth_Authenticate_Handler(srv interface{}, ctx context.Context, dec func(i
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Auth_CheckPerm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CheckPermRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AuthServer).CheckPerm(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Auth_CheckPerm_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AuthServer).CheckPerm(ctx, req.(*CheckPermRequest))
}
return interceptor(ctx, in, info, handler)
}
// Auth_ServiceDesc is the grpc.ServiceDesc for Auth service. // Auth_ServiceDesc is the grpc.ServiceDesc for Auth service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -136,10 +103,6 @@ var Auth_ServiceDesc = grpc.ServiceDesc{
MethodName: "Authenticate", MethodName: "Authenticate",
Handler: _Auth_Authenticate_Handler, Handler: _Auth_Authenticate_Handler,
}, },
{
MethodName: "CheckPerm",
Handler: _Auth_CheckPerm_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "auth.proto", Metadata: "auth.proto",

View File

@ -6,7 +6,6 @@ import (
"github.com/samber/lo" "github.com/samber/lo"
"github.com/spf13/viper" "github.com/spf13/viper"
"gorm.io/datatypes"
) )
type Account struct { type Account struct {
@ -18,7 +17,7 @@ type Account struct {
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Banner string `json:"banner"` Banner string `json:"banner"`
ConfirmedAt *time.Time `json:"confirmed_at"` ConfirmedAt *time.Time `json:"confirmed_at"`
PermNodes datatypes.JSONMap `json:"perm_nodes"` PowerLevel int `json:"power_level"`
Profile AccountProfile `json:"profile"` Profile AccountProfile `json:"profile"`
PersonalPage AccountPage `json:"personal_page"` PersonalPage AccountPage `json:"personal_page"`

View File

@ -60,5 +60,5 @@ func (v AuthTicket) IsAvailable() error {
type AuthContext struct { type AuthContext struct {
Ticket AuthTicket `json:"ticket"` Ticket AuthTicket `json:"ticket"`
Account Account `json:"account"` Account Account `json:"account"`
LastUsedAt time.Time `json:"last_used_at"` ExpiredAt time.Time `json:"expired_at"`
} }

View File

@ -108,8 +108,6 @@ func editUserinfo(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }
services.InvalidAuthCacheWithUser(account.ID)
return c.SendStatus(fiber.StatusOK) return c.SendStatus(fiber.StatusOK)
} }

View File

@ -42,12 +42,11 @@ func authFunc(c *fiber.Ctx, overrides ...string) error {
} }
rtk := c.Cookies(services.CookieRefreshKey) rtk := c.Cookies(services.CookieRefreshKey)
if ctx, perms, atk, rtk, err := services.Authenticate(token, rtk, 0); err == nil { if user, atk, rtk, err := services.Authenticate(token, rtk, 0); err == nil {
if atk != token { if atk != token {
services.SetJwtCookieSet(c, atk, rtk) services.SetJwtCookieSet(c, atk, rtk)
} }
c.Locals("permissions", perms) c.Locals("principal", user)
c.Locals("principal", ctx.Account)
return nil return nil
} else { } else {
return err return err

View File

@ -46,8 +46,8 @@ func listAvailableRealm(c *fiber.Ctx) error {
func createRealm(c *fiber.Ctx) error { func createRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account) user := c.Locals("principal").(models.Account)
if err := utils.CheckPermissions(c, "CreateRealms", true); err != nil { if user.PowerLevel < 10 {
return err return fiber.NewError(fiber.StatusForbidden, "require power level 10 to create realms")
} }
var data struct { var data struct {

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"git.solsynth.dev/hydrogen/passport/pkg/database" "git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models" "git.solsynth.dev/hydrogen/passport/pkg/models"
"git.solsynth.dev/hydrogen/passport/pkg/services"
"git.solsynth.dev/hydrogen/passport/pkg/utils" "git.solsynth.dev/hydrogen/passport/pkg/utils"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
@ -93,8 +92,6 @@ func personalizeAction(c *fiber.Ctx) error {
}).Redirect("/users/me/personalize") }).Redirect("/users/me/personalize")
} }
services.InvalidAuthCacheWithUser(account.ID)
return flash.WithInfo(c, fiber.Map{ return flash.WithInfo(c, fiber.Map{
"message": "your account has been personalized", "message": "your account has been personalized",
}).Redirect("/users/me") }).Redirect("/users/me")

View File

@ -42,7 +42,6 @@ func listenWebsocket(c *websocket.Conn) {
var req struct { var req struct {
RequestID string `json:"request_id"` RequestID string `json:"request_id"`
KeypairID string `json:"keypair_id"` KeypairID string `json:"keypair_id"`
Algorithm string `json:"algorithm"`
OwnerID uint `json:"owner_id"` OwnerID uint `json:"owner_id"`
Deadline int64 `json:"deadline"` Deadline int64 `json:"deadline"`
} }
@ -50,12 +49,11 @@ func listenWebsocket(c *websocket.Conn) {
if len(req.RequestID) <= 0 || len(req.KeypairID) <= 0 || req.OwnerID <= 0 { if len(req.RequestID) <= 0 || len(req.KeypairID) <= 0 || req.OwnerID <= 0 {
message = lo.ToPtr(models.UnifiedCommandFromError(fmt.Errorf("invalid request"))) message = lo.ToPtr(models.UnifiedCommandFromError(fmt.Errorf("invalid request")))
} }
services.KexRequest(c, req.RequestID, req.KeypairID, req.Algorithm, req.OwnerID, req.Deadline) services.KexRequest(c, req.RequestID, req.KeypairID, req.OwnerID, req.Deadline)
case "kex.provide": case "kex.provide":
var req struct { var req struct {
RequestID string `json:"request_id"` RequestID string `json:"request_id"`
KeypairID string `json:"keypair_id"` KeypairID string `json:"keypair_id"`
Algorithm string `json:"algorithm"`
PublicKey []byte `json:"public_key"` PublicKey []byte `json:"public_key"`
} }
_ = jsoniter.Unmarshal(payload, &req) _ = jsoniter.Unmarshal(payload, &req)

View File

@ -2,8 +2,6 @@ package services
import ( import (
"fmt" "fmt"
"github.com/spf13/viper"
"gorm.io/datatypes"
"time" "time"
"git.solsynth.dev/hydrogen/passport/pkg/database" "git.solsynth.dev/hydrogen/passport/pkg/database"
@ -68,7 +66,7 @@ func CreateAccount(name, nick, email, password string) (models.Account, error) {
VerifiedAt: nil, VerifiedAt: nil,
}, },
}, },
PermNodes: datatypes.JSONMap(viper.GetStringMap("permissions.default")), PowerLevel: 0,
ConfirmedAt: nil, ConfirmedAt: nil,
} }
@ -100,14 +98,7 @@ func ConfirmAccount(code string) error {
return database.C.Transaction(func(tx *gorm.DB) error { return database.C.Transaction(func(tx *gorm.DB) error {
user.ConfirmedAt = lo.ToPtr(time.Now()) user.ConfirmedAt = lo.ToPtr(time.Now())
user.PowerLevel += 5
for k, v := range viper.GetStringMap("permissions.verified") {
if val, ok := user.PermNodes[k]; !ok {
user.PermNodes[k] = v
} else if !ComparePermNode(val, v) {
user.PermNodes[k] = v
}
}
if err := database.C.Delete(&token).Error; err != nil { if err := database.C.Delete(&token).Error; err != nil {
return err return err
@ -116,8 +107,6 @@ func ConfirmAccount(code string) error {
return err return err
} }
InvalidAuthCacheWithUser(user.ID)
return nil return nil
}) })
} }

View File

@ -4,14 +4,17 @@ import (
"fmt" "fmt"
"time" "time"
"git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models" "git.solsynth.dev/hydrogen/passport/pkg/models"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
jsoniter "github.com/json-iterator/go"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"go.etcd.io/bbolt"
) )
var authContextCache = make(map[string]models.AuthContext) const authContextBucket = "AuthContext"
func Authenticate(access, refresh string, depth int) (ctx models.AuthContext, perms map[string]any, newAccess, newRefresh string, err error) { func Authenticate(access, refresh string, depth int) (user models.Account, newAccess, newRefresh string, err error) {
var claims PayloadClaims var claims PayloadClaims
claims, err = DecodeJwt(access) claims, err = DecodeJwt(access)
if err != nil { if err != nil {
@ -29,8 +32,19 @@ func Authenticate(access, refresh string, depth int) (ctx models.AuthContext, pe
newAccess = access newAccess = access
newRefresh = refresh newRefresh = refresh
if ctx, err = GetAuthContext(claims.ID); err == nil { var ctx models.AuthContext
perms = FilterPermNodes(ctx.Account.PermNodes, ctx.Ticket.Claims)
ctx, lookupErr := GetAuthContext(claims.ID)
if lookupErr == nil {
log.Debug().Str("jti", claims.ID).Msg("Hit auth context cache once!")
user = ctx.Account
return
}
ctx, err = GrantAuthContext(claims.ID)
if err == nil {
log.Debug().Str("jti", claims.ID).Err(lookupErr).Msg("Missed auth context cache once!")
user = ctx.Account
return return
} }
@ -42,20 +56,32 @@ func GetAuthContext(jti string) (models.AuthContext, error) {
var err error var err error
var ctx models.AuthContext var ctx models.AuthContext
if val, ok := authContextCache[jti]; ok { err = database.B.View(func(tx *bbolt.Tx) error {
ctx = val bucket := tx.Bucket([]byte(authContextBucket))
ctx.LastUsedAt = time.Now() if bucket == nil {
authContextCache[jti] = ctx return fmt.Errorf("unable to find auth context bucket")
log.Debug().Str("jti", jti).Msg("Used an auth context cache") }
} else {
ctx, err = CacheAuthContext(jti) raw := bucket.Get([]byte(jti))
log.Debug().Str("jti", jti).Msg("Created a new auth context cache") if raw == nil {
return fmt.Errorf("unable to find auth context")
} else if err := jsoniter.Unmarshal(raw, &ctx); err != nil {
return fmt.Errorf("unable to unmarshal auth context: %v", err)
}
return nil
})
if err == nil && time.Now().Unix() >= ctx.ExpiredAt.Unix() {
_ = RevokeAuthContext(jti)
return ctx, fmt.Errorf("auth context has been expired")
} }
return ctx, err return ctx, err
} }
func CacheAuthContext(jti string) (models.AuthContext, error) { func GrantAuthContext(jti string) (models.AuthContext, error) {
var ctx models.AuthContext var ctx models.AuthContext
// Query data from primary database // Query data from primary database
@ -71,37 +97,37 @@ func CacheAuthContext(jti string) (models.AuthContext, error) {
return ctx, fmt.Errorf("invalid account: %v", err) return ctx, fmt.Errorf("invalid account: %v", err)
} }
// Every context should expires in some while
// Once user update their account info, this will have delay to update
ctx = models.AuthContext{ ctx = models.AuthContext{
Ticket: ticket, Ticket: ticket,
Account: user, Account: user,
LastUsedAt: time.Now(), ExpiredAt: time.Now().Add(5 * time.Minute),
} }
// Put the data into memory for cache // Save data into KV cache
authContextCache[jti] = ctx return ctx, database.B.Update(func(tx *bbolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte(authContextBucket))
return ctx, nil if err != nil {
return err
} }
func RecycleAuthContext() { raw, err := jsoniter.Marshal(ctx)
if len(authContextCache) == 0 { if err != nil {
return return err
} }
affected := 0 return bucket.Put([]byte(jti), raw)
for key, val := range authContextCache { })
if val.LastUsedAt.Add(60*time.Second).Unix() < time.Now().Unix() {
affected++
delete(authContextCache, key)
}
}
log.Debug().Int("affected", affected).Msg("Recycled auth context...")
} }
func InvalidAuthCacheWithUser(userId uint) { func RevokeAuthContext(jti string) error {
for key, val := range authContextCache { return database.B.Update(func(tx *bbolt.Tx) error {
if val.Account.ID == userId { bucket, err := tx.CreateBucketIfNotExists([]byte(authContextBucket))
delete(authContextCache, key) if err != nil {
} return err
} }
return bucket.Delete([]byte(jti))
})
} }

View File

@ -15,7 +15,7 @@ type kexRequest struct {
var kexRequests = make(map[string]map[string]kexRequest) var kexRequests = make(map[string]map[string]kexRequest)
func KexRequest(conn *websocket.Conn, requestId, keypairId, algorithm string, ownerId uint, deadline int64) { func KexRequest(conn *websocket.Conn, requestId, keypairId string, ownerId uint, deadline int64) {
if kexRequests[keypairId] == nil { if kexRequests[keypairId] == nil {
kexRequests[keypairId] = make(map[string]kexRequest) kexRequests[keypairId] = make(map[string]kexRequest)
} }
@ -38,7 +38,6 @@ func KexRequest(conn *websocket.Conn, requestId, keypairId, algorithm string, ow
Payload: fiber.Map{ Payload: fiber.Map{
"request_id": requestId, "request_id": requestId,
"keypair_id": keypairId, "keypair_id": keypairId,
"algorithm": algorithm,
"owner_id": ownerId, "owner_id": ownerId,
"deadline": deadline, "deadline": deadline,
}, },

View File

@ -1,63 +0,0 @@
package services
import (
"fmt"
"reflect"
"regexp"
"strings"
)
func HasPermNode(perms map[string]any, requiredKey string, requiredValue any) bool {
if heldValue, ok := perms[requiredKey]; ok {
return ComparePermNode(heldValue, requiredValue)
}
return false
}
func ComparePermNode(held any, required any) bool {
heldValue := reflect.ValueOf(held)
requiredValue := reflect.ValueOf(required)
switch heldValue.Kind() {
case reflect.Int, reflect.Float64:
if heldValue.Float() >= requiredValue.Float() {
return true
}
case reflect.String:
if heldValue.String() == requiredValue.String() {
return true
}
case reflect.Slice, reflect.Array:
for i := 0; i < heldValue.Len(); i++ {
if reflect.DeepEqual(heldValue.Index(i).Interface(), required) {
return true
}
}
default:
if reflect.DeepEqual(held, required) {
return true
}
}
return false
}
func FilterPermNodes(tree map[string]any, claims []string) map[string]any {
filteredTree := make(map[string]any)
match := func(claim, permission string) bool {
regex := strings.ReplaceAll(claim, "*", ".*")
match, _ := regexp.MatchString(fmt.Sprintf("^%s$", regex), permission)
return match
}
for _, claim := range claims {
for key, value := range tree {
if match(claim, key) {
filteredTree[key] = value
}
}
}
return filteredTree
}

View File

@ -3,8 +3,10 @@ package services
import ( import (
"git.solsynth.dev/hydrogen/passport/pkg/database" "git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models" "git.solsynth.dev/hydrogen/passport/pkg/models"
jsoniter "github.com/json-iterator/go"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.etcd.io/bbolt"
"time" "time"
) )
@ -22,3 +24,36 @@ func DoAutoSignoff() {
log.Debug().Int64("affected", tx.RowsAffected).Msg("Auto sign off accomplished.") log.Debug().Int64("affected", tx.RowsAffected).Msg("Auto sign off accomplished.")
} }
} }
func DoAutoAuthCleanup() {
log.Debug().Msg("Now cleaning up cached auth context...")
count := 0
err := database.B.Batch(func(tx *bbolt.Tx) error {
bucket := tx.Bucket([]byte(authContextBucket))
if bucket == nil {
return nil
}
cursor := bucket.Cursor()
var ctx models.AuthContext
for key, val := cursor.First(); key != nil; key, val = cursor.Next() {
if err := jsoniter.Unmarshal(val, &ctx); err != nil {
bucket.Delete(key)
count++
} else if time.Now().Unix() >= ctx.ExpiredAt.Unix() {
bucket.Delete(key)
count++
}
}
return nil
})
if err != nil {
log.Error().Err(err).Msg("An error occurred when running auth context cleanup...")
} else {
log.Debug().Int("affected", count).Msg("Clean up auth context accomplished.")
}
}

View File

@ -1,8 +1,6 @@
package utils package utils
import ( import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/services"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/samber/lo" "github.com/samber/lo"
@ -21,17 +19,6 @@ func BindAndValidate(c *fiber.Ctx, out any) error {
return nil 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 { func GetRedirectUri(c *fiber.Ctx, fallback ...string) *string {
if len(c.Query("redirect_uri")) > 0 { if len(c.Query("redirect_uri")) > 0 {
return lo.ToPtr(c.Query("redirect_uri")) return lo.ToPtr(c.Query("redirect_uri"))

View File

@ -34,9 +34,4 @@ refresh_token_duration = 2592000
[database] [database]
dsn = "host=localhost dbname=hy_passport port=5432 sslmode=disable" dsn = "host=localhost dbname=hy_passport port=5432 sslmode=disable"
prefix = "passport_" prefix = "passport_"
bolt = "uploads/bolt.db"
[permissions.default]
CreatePaperclipAttachments = 1048576
[permissions.verified]
CreatePaperclipAttachments = 26214400