feat(system): add config files and implement pagination · origadmin/admin@1a277f0 · GitHub
Skip to content

Commit 1a277f0

Browse files
committed
feat(system): add config files and implement pagination
- Add config files for system service - Implement pagination logic in db helper - Update DAL files to use new pagination logic - Refactor security bridge and loader bootstrap
1 parent d0f698b commit 1a277f0

17 files changed

Lines changed: 411 additions & 78 deletions

File tree

helpers/db/db.go

Lines changed: 21 additions & 1 deletion

helpers/securityx/security.go

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,32 @@ func NewAuthorizer(bootstrap *configs.Bootstrap, ss ...casbin.Setting) (security
4747
return authorizer, nil
4848
}
4949

50-
type Data interface {
50+
type DataProvider interface {
5151
QueryRoles(ctx context.Context, subject string) ([]string, error)
5252
QueryPermissions(ctx context.Context, subject string) ([]string, error)
5353
}
5454

55+
type SecurityConfig struct {
56+
// Authenticator is the authenticator used for the authorization header.
57+
Authenticator security.Authenticator
58+
// Authorizer is the authorizer used for the authorization header.
59+
Authorizer security.Authorizer
60+
}
61+
62+
type Option = func(config *SecurityConfig)
63+
64+
func WithAuthenticator(authenticator security.Authenticator) Option {
65+
return func(config *SecurityConfig) {
66+
config.Authenticator = authenticator
67+
}
68+
}
69+
70+
func WithAuthorizer(authorizer security.Authorizer) Option {
71+
return func(config *SecurityConfig) {
72+
config.Authorizer = authorizer
73+
}
74+
}
75+
5576
type SecurityBridge struct {
5677
// TokenSource is the type of the token.
5778
TokenSource security.TokenSource
@@ -67,12 +88,12 @@ type SecurityBridge struct {
6788
SkipKey string
6889
// PublicPaths are the public paths that do not require authentication.
6990
PublicPaths []string
91+
// Provider is the role/permission data from the database.
92+
Provider DataProvider
7093
// Skipper is the function used to skip authentication.
7194
Skipper func(string) bool
7295
// IsRoot is the function used to check if the request is root.
7396
IsRoot func(ctx context.Context, claims security.Claims) bool
74-
// Data is the permission data from the database.
75-
Data Data
7697
// TokenParser is the parser used to parse the token from the context.
7798
TokenParser func(ctx context.Context) string
7899
// PolicyParser is the parser used to parse the policy from the context.
@@ -136,7 +157,7 @@ func (obj SecurityBridge) aggregateTokenParsers(outer ...func(ctx context.Contex
136157
func (obj SecurityBridge) Build() middleware.KMiddleware {
137158
if obj.TokenParser == nil {
138159
obj.TokenParser = obj.aggregateTokenParsers(
139-
FromTransportClient(obj.AuthenticationHeader, obj.Scheme.String()),
160+
//FromTransportClient(obj.AuthenticationHeader, obj.Scheme.String()),
140161
FromTransportServer(obj.AuthenticationHeader, obj.Scheme.String()),
141162
)
142163
}
@@ -197,14 +218,14 @@ func (obj SecurityBridge) policyParser(ctx context.Context, claims security.Clai
197218
return obj.PolicyParser(ctx, claims)
198219
}
199220
log.Debugf("PolicyParser: parsing policy for subject: %s", claims.GetSubject())
200-
roles, err := obj.Data.QueryRoles(ctx, claims.GetSubject())
221+
roles, err := obj.Provider.QueryRoles(ctx, claims.GetSubject())
201222
if err != nil {
202223
log.Errorf("PolicyParser: failed to query roles for subject: %s, error: %s", claims.GetSubject(), err.Error())
203224
return nil, err
204225
}
205226
log.Debugf("PolicyParser: queried roles for subject: %s, roles: %v", claims.GetSubject(), roles)
206227

207-
permissions, err := obj.Data.QueryPermissions(ctx, claims.GetSubject())
228+
permissions, err := obj.Provider.QueryPermissions(ctx, claims.GetSubject())
208229
if err != nil {
209230
log.Errorf("PolicyParser: failed to query permissions for subject: %s, error: %s", claims.GetSubject(), err.Error())
210231
return nil, err
@@ -252,3 +273,21 @@ func FromTransportServer(authorize string, scheme string) func(ctx context.Conte
252273
return ""
253274
}
254275
}
276+
277+
func DefaultBridge() *SecurityBridge {
278+
bridge := SecurityBridge{
279+
TokenSource: security.TokenSourceHeader,
280+
Scheme: security.SchemeBearer,
281+
AuthenticationHeader: security.HeaderAuthorize,
282+
SkipKey: msecurity.MetadataSecuritySkipKey,
283+
PublicPaths: nil,
284+
Skipper: func(path string) bool {
285+
return false
286+
},
287+
IsRoot: func(ctx context.Context, claims security.Claims) bool {
288+
return claims.GetSubject() == "root" || claims.GetSubject() == "admin"
289+
},
290+
TokenParser: nil,
291+
}
292+
return &bridge
293+
}

internal/loader/bootstrap.go

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ package loader
88
import (
99
"fmt"
1010
"os"
11+
"path/filepath"
12+
"strings"
1113

1214
"github.com/goexts/generic/settings"
1315
"github.com/origadmin/runtime"
@@ -20,6 +22,48 @@ import (
2022
"origadmin/application/admin/internal/configs"
2123
)
2224

25+
// decodeFile loads the config file from the given path
26+
func decodeFile(path string, cfg any) error {
27+
// skip stupid temp file
28+
if strings.HasSuffix(path, "~") || strings.HasSuffix(path, ".bak") || strings.HasSuffix(path, ".tmp") || strings.HasSuffix(path, ".lock") {
29+
return nil
30+
}
31+
// Decode the file into the config struct
32+
if err := codec.DecodeFromFile(path, cfg); err != nil {
33+
return errors.Wrapf(err, "failed to parse config file %s", path)
34+
}
35+
return nil
36+
}
37+
38+
// decodeDir loads the config file from the given directory
39+
func decodeDir(path string, cfg any) error {
40+
found := false
41+
// Walk through the directory and load each file
42+
err := filepath.WalkDir(path, func(walkpath string, d os.DirEntry, err error) error {
43+
if err != nil {
44+
return errors.Wrapf(err, "failed to get config file %s", walkpath)
45+
}
46+
// Check if the path is a directory
47+
if d.IsDir() {
48+
return nil
49+
}
50+
51+
// Decode the file into the config struct
52+
if err := decodeFile(walkpath, cfg); err != nil {
53+
return err
54+
}
55+
found = true
56+
return nil
57+
})
58+
if err != nil {
59+
return errors.Wrap(err, "load config error")
60+
}
61+
if !found {
62+
return errors.New("no config file found in " + path)
63+
}
64+
return nil
65+
}
66+
2367
// LoadFileBootstrap load config from file
2468
func LoadFileBootstrap(path string) (*configs.Bootstrap, error) {
2569
typo := codec.TypeFromPath(path)
@@ -28,11 +72,10 @@ func LoadFileBootstrap(path string) (*configs.Bootstrap, error) {
2872
}
2973

3074
cfg := DefaultBootstrap()
31-
err := codec.DecodeFromFile(path, cfg)
75+
err := decodeFile(path, cfg)
3276
if err != nil {
3377
return nil, err
3478
}
35-
3679
return cfg, nil
3780
}
3881

@@ -46,8 +89,13 @@ func LoadLocalBootstrap(source *configv1.SourceConfig) (*configs.Bootstrap, erro
4689
if err != nil {
4790
return nil, errors.Wrapf(err, "failed to state file %s", path)
4891
}
92+
var bs configs.Bootstrap
4993
if stat.IsDir() {
50-
return nil, errors.String("file config is a directory")
94+
err := decodeDir(path, &bs)
95+
if err != nil {
96+
return nil, err
97+
}
98+
return &bs, nil
5199
}
52100
return LoadFileBootstrap(path)
53101
}
@@ -79,7 +127,7 @@ func LoadBootstrap(flags *Bootstrap) (*configs.Bootstrap, error) {
79127
SetupEnv(sourceConfig.EnvArgs, sourceConfig.EnvPrefixes[0])
80128
}
81129

82-
log.Infof("load soure config: %+v", sourceConfig)
130+
log.Infof("load source config: %+v", sourceConfig)
83131
var bs *configs.Bootstrap
84132
switch sourceConfig.GetType() {
85133
case "file":
@@ -90,10 +138,11 @@ func LoadBootstrap(flags *Bootstrap) (*configs.Bootstrap, error) {
90138
if err != nil {
91139
return nil, err
92140
}
141+
93142
if err := ReplaceObject(bs, sourceConfig.EnvArgs); err != nil {
94143
return nil, err
95144
}
96-
145+
log.Infof("load config: %+v\n", bs)
97146
return bs, nil
98147
}
99148

internal/loader/bootstrap_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ func TestAddRoleResource(t *testing.T) {
319319
pr, err := client.PermissionResource.Create().
320320
SetPermission(perm).
321321
SetResource(res).
322-
SetActions("abc123").Save(context.Background())
322+
Save(context.Background())
323323
//perm, err := perm.Update().AddPermissionResources().Save(context.Background())
324324
if err != nil {
325325
t.Fatalf("failed adding resource to permission: %v", err)

internal/mods/agent/http.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func NewHTTPServerAgent(bootstrap *configs.Bootstrap, registrars []ServerRegiste
7676
IsRoot: func(ctx context.Context, claims security.Claims) bool {
7777
return claims.GetSubject() == "root" || claims.GetSubject() == "admin"
7878
},
79-
Data: &data{},
79+
Provider: &data{},
8080
TokenParser: nil,
8181
}
8282
serv := selector.Server(bridge.Build()).Match(func(ctx context.Context, operation string) bool {

internal/mods/system/dal/auth.dal.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ import (
99
"errors"
1010
"sync"
1111

12-
"entgo.io/ent/dialect/sql"
1312
"github.com/origadmin/runtime/log"
1413
"github.com/origadmin/toolkits/security"
1514

1615
pb "origadmin/application/admin/api/v1/services/system"
1716
"origadmin/application/admin/internal/mods/system/dal/entity/ent"
18-
"origadmin/application/admin/internal/mods/system/dal/entity/ent/resource"
1917
"origadmin/application/admin/internal/mods/system/dto"
2018
)
2119

@@ -147,14 +145,6 @@ func authResourceQueryOptions(query *ent.ResourceQuery, option dto.AuthResourceQ
147145
return query
148146
}
149147

150-
func authResourceOrderBy(fields []string, opts ...sql.OrderTermOption) []resource.OrderOption {
151-
var orders []resource.OrderOption
152-
for _, field := range fields {
153-
orders = append(orders, sql.OrderByField(field, opts...).ToFunc())
154-
}
155-
return orders
156-
}
157-
158148
type refreshTokenizer struct {
159149
tokenizer security.Tokenizer
160150
}

internal/mods/system/dal/permission.dal.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/origadmin/runtime/log"
1212

1313
pb "origadmin/application/admin/api/v1/services/system"
14+
"origadmin/application/admin/helpers/db"
1415
"origadmin/application/admin/internal/mods/system/dal/entity/ent"
1516
"origadmin/application/admin/internal/mods/system/dal/entity/ent/permission"
1617
"origadmin/application/admin/internal/mods/system/dto"
@@ -123,7 +124,7 @@ func permissionPageQuery(ctx context.Context, query *ent.PermissionQuery, in *pb
123124
if err != nil {
124125
return nil, 0, err
125126
}
126-
query = permissionQueryPage(query, in)
127+
query = db.PaginationQuery(query, in, !in.NoPaging)
127128
result, err := query.All(ctx)
128129
return dto.ConvertPermissions(result), int32(count), err
129130
}

internal/mods/system/dal/personal.dal.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,12 @@ func NewPersonalRepo(db *Data, logger log.KLogger) dto.PersonalRepo {
9999
}
100100

101101
func personalPageQuery(ctx context.Context, query *ent.ResourceQuery, in *pb.ListResourcesRequest, option dto.ResourceQueryOption) ([]*dto.ResourcePB, int32, error) {
102-
query = personalQueryPage(query, in)
103102
query = personalQueryOptions(query, option)
104103
count, err := query.Count(ctx)
105104
if err != nil {
106105
return nil, 0, err
107106
}
107+
query = personalQueryPage(query, in)
108108
result, err := query.All(ctx)
109109
return dto.ConvertResources(result), int32(count), err
110110
}

internal/mods/system/dal/resource.dal.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,12 @@ func resourcePageQuery(ctx context.Context, query *ent.ResourceQuery, in *pb.Lis
128128
if err != nil {
129129
return nil, 0, err
130130
}
131-
query = resourceQueryPage(query, in)
131+
query = db.PaginationQuery(query, in, !in.NoPaging)
132132
result, err := query.All(ctx)
133133
return dto.ConvertResources(result), int32(count), err
134134
}
135135

136-
func resourceQueryPage(query *ent.ResourceQuery, in *pb.ListResourcesRequest) *ent.ResourceQuery {
137-
if in.NoPaging {
138-
return db.NoPageQuery(query, in)
139-
}
140-
return db.PageQuery(query, in)
141-
}
142-
143-
func orderBy(orders []string) []resource.OrderOption {
136+
func resourceOrderBy(orders []string) []resource.OrderOption {
144137
return db.OrderBy[resource.OrderOption](orders)
145138
}
146139

@@ -152,7 +145,7 @@ func resourceQueryOptions(query *ent.ResourceQuery, option dto.ResourceQueryOpti
152145
query = query.Omit(option.OmitFields...).ResourceQuery
153146
}
154147
if len(option.OrderFields) > 0 {
155-
query = query.Order(orderBy(option.OrderFields)...)
148+
query = query.Order(resourceOrderBy(option.OrderFields)...)
156149
}
157150
return query
158151
}

internal/mods/system/dal/role.dal.go

Lines changed: 2 additions & 21 deletions

0 commit comments

Comments
 (0)