diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index d12a850..82c913b 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,9 +4,9 @@
-
+
-
+
@@ -139,8 +139,6 @@
-
-
@@ -164,7 +162,9 @@
-
+
+
+
true
diff --git a/pkg/grpc/auth.go b/pkg/grpc/auth.go
index 87807d8..1f00a7d 100644
--- a/pkg/grpc/auth.go
+++ b/pkg/grpc/auth.go
@@ -3,6 +3,7 @@ package grpc
import (
"context"
"fmt"
+ jsoniter "github.com/json-iterator/go"
"git.solsynth.dev/hydrogen/passport/pkg/grpc/proto"
"git.solsynth.dev/hydrogen/passport/pkg/services"
@@ -10,16 +11,18 @@ import (
)
func (v *Server) Authenticate(_ context.Context, in *proto.AuthRequest) (*proto.AuthReply, error) {
- user, atk, rtk, err := services.Authenticate(in.GetAccessToken(), in.GetRefreshToken(), 0)
+ user, perms, atk, rtk, err := services.Authenticate(in.GetAccessToken(), in.GetRefreshToken(), 0)
if err != nil {
return &proto.AuthReply{
IsValid: false,
}, nil
} else {
+ rawPerms, _ := jsoniter.Marshal(perms)
return &proto.AuthReply{
IsValid: true,
AccessToken: &atk,
RefreshToken: &rtk,
+ Permissions: rawPerms,
Userinfo: &proto.Userinfo{
Id: uint64(user.ID),
Name: user.Name,
diff --git a/pkg/grpc/proto/auth.pb.go b/pkg/grpc/proto/auth.pb.go
index 1870972..cc9144a 100644
--- a/pkg/grpc/proto/auth.pb.go
+++ b/pkg/grpc/proto/auth.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
-// protoc v4.25.3
+// protoc v5.26.1
// source: auth.proto
package proto
@@ -179,6 +179,7 @@ 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"`
}
func (x *AuthReply) Reset() {
@@ -241,6 +242,13 @@ func (x *AuthReply) GetUserinfo() *Userinfo {
return nil
}
+func (x *AuthReply) GetPermissions() []byte {
+ if x != nil {
+ return x.Permissions
+ }
+ return nil
+}
+
var File_auth_proto protoreflect.FileDescriptor
var file_auth_proto_rawDesc = []byte{
@@ -264,7 +272,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, 0xda, 0x01, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x70, 0x6c,
+ 0x6b, 0x65, 0x6e, 0x22, 0x91, 0x02, 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,
@@ -275,15 +283,18 @@ 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,
- 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,
+ 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, 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, 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 (
diff --git a/pkg/grpc/proto/auth.proto b/pkg/grpc/proto/auth.proto
index 456dd2b..b0b8365 100644
--- a/pkg/grpc/proto/auth.proto
+++ b/pkg/grpc/proto/auth.proto
@@ -28,4 +28,5 @@ message AuthReply {
optional string access_token = 2;
optional string refresh_token = 3;
optional Userinfo userinfo = 4;
+ optional bytes permissions = 5;
}
\ No newline at end of file
diff --git a/pkg/grpc/proto/auth_grpc.pb.go b/pkg/grpc/proto/auth_grpc.pb.go
index 3df2f5d..b794591 100644
--- a/pkg/grpc/proto/auth_grpc.pb.go
+++ b/pkg/grpc/proto/auth_grpc.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
-// - protoc v4.25.3
+// - protoc v5.26.1
// source: auth.proto
package proto
diff --git a/pkg/models/accounts.go b/pkg/models/accounts.go
index 12d2bff..d1f506d 100644
--- a/pkg/models/accounts.go
+++ b/pkg/models/accounts.go
@@ -6,18 +6,19 @@ import (
"github.com/samber/lo"
"github.com/spf13/viper"
+ "gorm.io/datatypes"
)
type Account struct {
BaseModel
- Name string `json:"name" gorm:"uniqueIndex"`
- Nick string `json:"nick"`
- Description string `json:"description"`
- Avatar string `json:"avatar"`
- Banner string `json:"banner"`
- ConfirmedAt *time.Time `json:"confirmed_at"`
- PowerLevel int `json:"power_level"`
+ Name string `json:"name" gorm:"uniqueIndex"`
+ Nick string `json:"nick"`
+ Description string `json:"description"`
+ Avatar string `json:"avatar"`
+ Banner string `json:"banner"`
+ ConfirmedAt *time.Time `json:"confirmed_at"`
+ PermNodes datatypes.JSONMap `json:"perm_nodes"`
Profile AccountProfile `json:"profile"`
PersonalPage AccountPage `json:"personal_page"`
diff --git a/pkg/server/auth_middleware.go b/pkg/server/auth_middleware.go
index adb133b..0576110 100644
--- a/pkg/server/auth_middleware.go
+++ b/pkg/server/auth_middleware.go
@@ -42,10 +42,11 @@ func authFunc(c *fiber.Ctx, overrides ...string) error {
}
rtk := c.Cookies(services.CookieRefreshKey)
- if user, atk, rtk, err := services.Authenticate(token, rtk, 0); err == nil {
+ if user, perms, atk, rtk, err := services.Authenticate(token, rtk, 0); err == nil {
if atk != token {
services.SetJwtCookieSet(c, atk, rtk)
}
+ c.Locals("permissions", perms)
c.Locals("principal", user)
return nil
} else {
diff --git a/pkg/server/ws.go b/pkg/server/ws.go
index ae1bf31..0c18af9 100644
--- a/pkg/server/ws.go
+++ b/pkg/server/ws.go
@@ -42,6 +42,7 @@ 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"`
}
@@ -49,11 +50,12 @@ 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.OwnerID, req.Deadline)
+ services.KexRequest(c, req.RequestID, req.KeypairID, req.Algorithm, 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)
diff --git a/pkg/services/accounts.go b/pkg/services/accounts.go
index 63801aa..7739a8a 100644
--- a/pkg/services/accounts.go
+++ b/pkg/services/accounts.go
@@ -2,6 +2,8 @@ package services
import (
"fmt"
+ "github.com/spf13/viper"
+ "gorm.io/datatypes"
"time"
"git.solsynth.dev/hydrogen/passport/pkg/database"
@@ -66,7 +68,7 @@ func CreateAccount(name, nick, email, password string) (models.Account, error) {
VerifiedAt: nil,
},
},
- PowerLevel: 0,
+ PermNodes: datatypes.JSONMap(viper.GetStringMap("permissions.default")),
ConfirmedAt: nil,
}
@@ -98,7 +100,14 @@ func ConfirmAccount(code string) error {
return database.C.Transaction(func(tx *gorm.DB) error {
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 !HasPermNode(val, v) {
+ user.PermNodes[k] = v
+ }
+ }
if err := database.C.Delete(&token).Error; err != nil {
return err
diff --git a/pkg/services/auth.go b/pkg/services/auth.go
index 9141601..1cbf35a 100644
--- a/pkg/services/auth.go
+++ b/pkg/services/auth.go
@@ -14,7 +14,7 @@ import (
const authContextBucket = "AuthContext"
-func Authenticate(access, refresh string, depth int) (user models.Account, newAccess, newRefresh string, err error) {
+func Authenticate(access, refresh string, depth int) (user models.Account, perms map[string]any, newAccess, newRefresh string, err error) {
var claims PayloadClaims
claims, err = DecodeJwt(access)
if err != nil {
@@ -37,6 +37,7 @@ func Authenticate(access, refresh string, depth int) (user models.Account, newAc
ctx, lookupErr := GetAuthContext(claims.ID)
if lookupErr == nil {
log.Debug().Str("jti", claims.ID).Msg("Hit auth context cache once!")
+ perms = FilterPermNodes(ctx.Account.PermNodes, ctx.Ticket.Claims)
user = ctx.Account
return
}
@@ -44,6 +45,7 @@ func Authenticate(access, refresh string, depth int) (user models.Account, newAc
ctx, err = GrantAuthContext(claims.ID)
if err == nil {
log.Debug().Str("jti", claims.ID).Err(lookupErr).Msg("Missed auth context cache once!")
+ perms = FilterPermNodes(ctx.Account.PermNodes, ctx.Ticket.Claims)
user = ctx.Account
return
}
@@ -97,7 +99,7 @@ func GrantAuthContext(jti string) (models.AuthContext, error) {
return ctx, fmt.Errorf("invalid account: %v", err)
}
- // Every context should expires in some while
+ // Every context should expire in some while
// Once user update their account info, this will have delay to update
ctx = models.AuthContext{
Ticket: ticket,
diff --git a/pkg/services/e2ee.go b/pkg/services/e2ee.go
index 19b4d07..6197c02 100644
--- a/pkg/services/e2ee.go
+++ b/pkg/services/e2ee.go
@@ -15,7 +15,7 @@ type kexRequest struct {
var kexRequests = make(map[string]map[string]kexRequest)
-func KexRequest(conn *websocket.Conn, requestId, keypairId string, ownerId uint, deadline int64) {
+func KexRequest(conn *websocket.Conn, requestId, keypairId, algorithm string, ownerId uint, deadline int64) {
if kexRequests[keypairId] == nil {
kexRequests[keypairId] = make(map[string]kexRequest)
}
@@ -38,6 +38,7 @@ func KexRequest(conn *websocket.Conn, requestId, keypairId string, ownerId uint,
Payload: fiber.Map{
"request_id": requestId,
"keypair_id": keypairId,
+ "algorithm": algorithm,
"owner_id": ownerId,
"deadline": deadline,
},
diff --git a/pkg/services/perms.go b/pkg/services/perms.go
new file mode 100644
index 0000000..9a6914d
--- /dev/null
+++ b/pkg/services/perms.go
@@ -0,0 +1,55 @@
+package services
+
+import (
+ "reflect"
+ "regexp"
+ "strings"
+)
+
+func HasPermNode(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.Replace(permission, "*", ".*", -1)
+ match, _ := regexp.MatchString("^"+regex+"$", claim)
+ return match
+ }
+
+ for _, claim := range claims {
+ for key, value := range tree {
+ if match(claim, key) {
+ filteredTree[key] = value
+ }
+ }
+ }
+
+ return filteredTree
+}
diff --git a/settings.toml b/settings.toml
index 4430040..2b2bcf0 100644
--- a/settings.toml
+++ b/settings.toml
@@ -35,3 +35,9 @@ refresh_token_duration = 2592000
dsn = "host=localhost dbname=hy_passport port=5432 sslmode=disable"
prefix = "passport_"
bolt = "uploads/bolt.db"
+
+[permissions.default]
+CreatePaperclipAttachments = 1048576
+
+[permissions.verified]
+CreatePaperclipAttachments = 26214400
\ No newline at end of file