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
!<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" />
</component>
<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$/pkg/grpc/auth.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/grpc/auth.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" />
<change beforePath="$PROJECT_DIR$/pkg/services/notifications.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/services/notifications.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -46,32 +41,32 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"DefaultGoTemplateProperty": "Go File",
"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/Documents/Projects/Hydrogen/Passport/pkg/server/ui",
"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.lookFeel",
"vue.rearranger.settings.migration": "true"
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;DefaultGoTemplateProperty&quot;: &quot;Go File&quot;,
&quot;Go 构建.Backend.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.formatter.settings.were.checked&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.migrated.go.modules.settings&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.modules.automatic.dependencies.download&quot;: &quot;true&quot;,
&quot;RunOnceActivity.go.modules.go.list.on.any.changes.was.set&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;go.import.settings.migrated&quot;: &quot;true&quot;,
&quot;go.sdk.automatically.set&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/Users/littlesheep/Documents/Projects/Hydrogen/Passport/pkg/server/ui&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;run.code.analysis.last.selected.profile&quot;: &quot;pProject Default&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.lookFeel&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
},
"keyToStringList": {
"DatabaseDriversLRU": [
"postgresql"
&quot;keyToStringList&quot;: {
&quot;DatabaseDriversLRU&quot;: [
&quot;postgresql&quot;
]
}
}]]></component>
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/pkg/server/ui" />
@ -144,6 +139,11 @@
</option>
</component>
<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=":sparkles: Others userinfo" />
<MESSAGE value=":lipstick: Fix ui design" />
@ -164,12 +164,7 @@
<MESSAGE value=":sparkles: E2EE Key Exchange" />
<MESSAGE value=":bug: Bug fixes on E2EE" />
<MESSAGE value=":bug: Fix key exchange cause echo" />
<MESSAGE value=":bug: Fix notification push issue" />
<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" />
<option name="LAST_COMMIT_MESSAGE" value=":bug: Fix key exchange cause echo" />
</component>
<component name="VgoProject">
<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/template/html/v2 v2.1.1
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/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
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/rs/zerolog v1.31.0
github.com/samber/lo v1.39.0
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/text v0.14.0
google.golang.org/api v0.153.0
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
@ -48,6 +45,7 @@ require (
github.com/gofiber/utils v1.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // 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/googleapis/enterprise-certificate-proxy v0.3.2 // 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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 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/philhofer/fwd v1.1.2 // 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/pflag v1.0.5 // 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/valyala/bytebufferpool v1.0.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/sync v0.5.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/xerrors v0.0.0-20220907171357-04be3eba64a2 // 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/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
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.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
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/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.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/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
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/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
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/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
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.6.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/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
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 {
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
// All the things are optional so when error occurred the server won't crash
@ -67,8 +70,8 @@ func main() {
// Configure timed tasks
quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger)))
quartz.AddFunc("@every 60m", services.DoAutoSignoff)
quartz.AddFunc("@every 60m", services.DoAutoAuthCleanup)
quartz.AddFunc("@every 60m", services.DoAutoDatabaseCleanup)
quartz.AddFunc("@every 60s", services.RecycleAuthContext)
quartz.AddFunc("@every 5m", services.KexCleanup)
quartz.Start()
@ -82,4 +85,6 @@ func main() {
log.Info().Msgf("Passport v%s is quitting...", pkg.AppVersion)
quartz.Stop()
database.B.Close()
}

View File

@ -4,6 +4,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/samber/lo"
"github.com/spf13/viper"
"go.etcd.io/bbolt"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
@ -26,3 +27,14 @@ func NewGorm() error {
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 (
"context"
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/grpc/proto"
"git.solsynth.dev/hydrogen/passport/pkg/services"
jsoniter "github.com/json-iterator/go"
"github.com/samber/lo"
"github.com/spf13/viper"
)
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 {
return &proto.AuthReply{
IsValid: false,
}, nil
} else {
user := ctx.Account
rawPerms, _ := jsoniter.Marshal(perms)
return &proto.AuthReply{
IsValid: true,
AccessToken: &atk,
RefreshToken: &rtk,
Permissions: rawPerms,
TicketId: lo.ToPtr(uint64(ctx.Ticket.ID)),
Userinfo: &proto.Userinfo{
Id: uint64(user.ID),
Name: user.Name,
@ -37,23 +32,3 @@ func (v *Server) Authenticate(_ context.Context, in *proto.AuthRequest) (*proto.
}, 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.
// versions:
// protoc-gen-go v1.33.0
// protoc v5.26.1
// protoc v4.25.3
// source: auth.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"`
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"`
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() {
@ -243,130 +241,6 @@ func (x *AuthReply) GetUserinfo() *Userinfo {
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_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,
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,
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,
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,
@ -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,
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,
0x12, 0x25, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18,
0x05, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x63, 0x6b, 0x65,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x48, 0x04, 0x52, 0x08, 0x74, 0x69,
0x63, 0x6b, 0x65, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x63,
0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72,
0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x0b, 0x0a, 0x09,
0x5f, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x65,
0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x69,
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,
0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f,
0x6b, 0x65, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f,
0x32, 0x3e, 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,
0x42, 0x09, 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
@ -442,22 +298,18 @@ func file_auth_proto_rawDescGZIP() []byte {
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{}{
(*Userinfo)(nil), // 0: proto.Userinfo
(*AuthRequest)(nil), // 1: proto.AuthRequest
(*AuthReply)(nil), // 2: proto.AuthReply
(*CheckPermRequest)(nil), // 3: proto.CheckPermRequest
(*CheckPermReply)(nil), // 4: proto.CheckPermReply
}
var file_auth_proto_depIdxs = []int32{
0, // 0: proto.AuthReply.userinfo:type_name -> proto.Userinfo
1, // 1: proto.Auth.Authenticate:input_type -> proto.AuthRequest
3, // 2: proto.Auth.CheckPerm:input_type -> proto.CheckPermRequest
2, // 3: proto.Auth.Authenticate:output_type -> proto.AuthReply
4, // 4: proto.Auth.CheckPerm:output_type -> proto.CheckPermReply
3, // [3:5] is the sub-list for method output_type
1, // [1:3] is the sub-list for method input_type
2, // 2: proto.Auth.Authenticate:output_type -> proto.AuthReply
2, // [2:3] is the sub-list for method output_type
1, // [1:2] 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 extendee
0, // [0:1] is the sub-list for field type_name
@ -505,30 +357,6 @@ func file_auth_proto_init() {
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[1].OneofWrappers = []interface{}{}
@ -539,7 +367,7 @@ func file_auth_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_auth_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},

View File

@ -6,7 +6,6 @@ package proto;
service Auth {
rpc Authenticate(AuthRequest) returns (AuthReply) {}
rpc CheckPerm(CheckPermRequest) returns (CheckPermReply) {}
}
message Userinfo {
@ -29,16 +28,4 @@ message AuthReply {
optional string access_token = 2;
optional string refresh_token = 3;
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.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v5.26.1
// - protoc v4.25.3
// source: auth.proto
package proto
@ -20,7 +20,6 @@ const _ = grpc.SupportPackageIsVersion7
const (
Auth_Authenticate_FullMethodName = "/proto.Auth/Authenticate"
Auth_CheckPerm_FullMethodName = "/proto.Auth/CheckPerm"
)
// 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.
type AuthClient interface {
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 {
@ -48,21 +46,11 @@ func (c *authClient) Authenticate(ctx context.Context, in *AuthRequest, opts ...
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.
// All implementations must embed UnimplementedAuthServer
// for forward compatibility
type AuthServer interface {
Authenticate(context.Context, *AuthRequest) (*AuthReply, error)
CheckPerm(context.Context, *CheckPermRequest) (*CheckPermReply, error)
mustEmbedUnimplementedAuthServer()
}
@ -73,9 +61,6 @@ type UnimplementedAuthServer struct {
func (UnimplementedAuthServer) Authenticate(context.Context, *AuthRequest) (*AuthReply, error) {
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() {}
// 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)
}
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.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@ -136,10 +103,6 @@ var Auth_ServiceDesc = grpc.ServiceDesc{
MethodName: "Authenticate",
Handler: _Auth_Authenticate_Handler,
},
{
MethodName: "CheckPerm",
Handler: _Auth_CheckPerm_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "auth.proto",

View File

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

View File

@ -60,5 +60,5 @@ func (v AuthTicket) IsAvailable() error {
type AuthContext struct {
Ticket AuthTicket `json:"ticket"`
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())
}
services.InvalidAuthCacheWithUser(account.ID)
return c.SendStatus(fiber.StatusOK)
}

View File

@ -42,12 +42,11 @@ func authFunc(c *fiber.Ctx, overrides ...string) error {
}
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 {
services.SetJwtCookieSet(c, atk, rtk)
}
c.Locals("permissions", perms)
c.Locals("principal", ctx.Account)
c.Locals("principal", user)
return nil
} else {
return err

View File

@ -46,8 +46,8 @@ 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 {
return err
if user.PowerLevel < 10 {
return fiber.NewError(fiber.StatusForbidden, "require power level 10 to create realms")
}
var data struct {

View File

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

View File

@ -42,7 +42,6 @@ func listenWebsocket(c *websocket.Conn) {
var req struct {
RequestID string `json:"request_id"`
KeypairID string `json:"keypair_id"`
Algorithm string `json:"algorithm"`
OwnerID uint `json:"owner_id"`
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 {
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":
var req struct {
RequestID string `json:"request_id"`
KeypairID string `json:"keypair_id"`
Algorithm string `json:"algorithm"`
PublicKey []byte `json:"public_key"`
}
_ = jsoniter.Unmarshal(payload, &req)

View File

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

View File

@ -4,14 +4,17 @@ import (
"fmt"
"time"
"git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models"
"github.com/gofiber/fiber/v2"
jsoniter "github.com/json-iterator/go"
"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
claims, err = DecodeJwt(access)
if err != nil {
@ -29,8 +32,19 @@ func Authenticate(access, refresh string, depth int) (ctx models.AuthContext, pe
newAccess = access
newRefresh = refresh
if ctx, err = GetAuthContext(claims.ID); err == nil {
perms = FilterPermNodes(ctx.Account.PermNodes, ctx.Ticket.Claims)
var ctx models.AuthContext
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
}
@ -42,20 +56,32 @@ func GetAuthContext(jti string) (models.AuthContext, error) {
var err error
var ctx models.AuthContext
if val, ok := authContextCache[jti]; ok {
ctx = val
ctx.LastUsedAt = time.Now()
authContextCache[jti] = ctx
log.Debug().Str("jti", jti).Msg("Used an auth context cache")
} else {
ctx, err = CacheAuthContext(jti)
log.Debug().Str("jti", jti).Msg("Created a new auth context cache")
err = database.B.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket([]byte(authContextBucket))
if bucket == nil {
return fmt.Errorf("unable to find auth context bucket")
}
raw := bucket.Get([]byte(jti))
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
}
func CacheAuthContext(jti string) (models.AuthContext, error) {
func GrantAuthContext(jti string) (models.AuthContext, error) {
var ctx models.AuthContext
// Query data from primary database
@ -71,37 +97,37 @@ func CacheAuthContext(jti string) (models.AuthContext, error) {
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{
Ticket: ticket,
Account: user,
LastUsedAt: time.Now(),
ExpiredAt: time.Now().Add(5 * time.Minute),
}
// Put the data into memory for cache
authContextCache[jti] = ctx
// Save data into KV cache
return ctx, database.B.Update(func(tx *bbolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte(authContextBucket))
if err != nil {
return err
}
return ctx, nil
raw, err := jsoniter.Marshal(ctx)
if err != nil {
return err
}
return bucket.Put([]byte(jti), raw)
})
}
func RecycleAuthContext() {
if len(authContextCache) == 0 {
return
func RevokeAuthContext(jti string) error {
return database.B.Update(func(tx *bbolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte(authContextBucket))
if err != nil {
return err
}
affected := 0
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) {
for key, val := range authContextCache {
if val.Account.ID == userId {
delete(authContextCache, key)
}
}
return bucket.Delete([]byte(jti))
})
}

View File

@ -15,7 +15,7 @@ type kexRequest struct {
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 {
kexRequests[keypairId] = make(map[string]kexRequest)
}
@ -38,7 +38,6 @@ func KexRequest(conn *websocket.Conn, requestId, keypairId, algorithm string, ow
Payload: fiber.Map{
"request_id": requestId,
"keypair_id": keypairId,
"algorithm": algorithm,
"owner_id": ownerId,
"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 (
"git.solsynth.dev/hydrogen/passport/pkg/database"
"git.solsynth.dev/hydrogen/passport/pkg/models"
jsoniter "github.com/json-iterator/go"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"go.etcd.io/bbolt"
"time"
)
@ -22,3 +24,36 @@ func DoAutoSignoff() {
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
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/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"))

View File

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