diff --git a/model/privilege.go b/model/privilege.go index 61b02b2..4e148b3 100644 --- a/model/privilege.go +++ b/model/privilege.go @@ -1,10 +1,15 @@ package model +import ( + "fmt" + "strings" +) + // platform:module:class:action -// pro:*:*:* -// pro:content:*:* -// pro:content:secret_news:* -// pro:content:secret_news:upload +// admin:*:*:* +// admin:audit:*:* +// admin:audit:flow:* +// admin:audit:flow:operate type Privilege struct { CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"` @@ -17,5 +22,26 @@ type Privilege struct { } func (p *Privilege) Validate() error { + ss := strings.Split(p.Code, ":") + + if len(ss) != 4 { + return fmt.Errorf("privilege must consist of four parts: (platform:module:class:action)") + } + + for _, s := range ss { + if len(s) == 0 { + return fmt.Errorf("privilege code parts must not be empty") + } + } + + code := strings.Clone(p.Code) + for strings.HasSuffix(code, ":*") { + code = code[:len(code)-2] + } + + if code != "*" && strings.Contains(code, "*") { + return fmt.Errorf("privilege can only have trailing wildcard search") + } + return nil } diff --git a/model/privilege_test.go b/model/privilege_test.go new file mode 100644 index 0000000..78bba46 --- /dev/null +++ b/model/privilege_test.go @@ -0,0 +1,74 @@ +package model + +import ( + "testing" + "uauth/internal/tool" +) + +func TestPrivilege_Validate(t *testing.T) { + type fields struct { + Code string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "全匹配", + fields: fields{Code: "*:*:*:*"}, + wantErr: false, + }, + { + name: "部分全匹配1", + fields: fields{Code: "user:ok:*:*"}, + wantErr: false, + }, + { + name: "部分全匹配2", + fields: fields{Code: "user:ok:nice:*"}, + wantErr: false, + }, + { + name: "中间全匹配1", + fields: fields{Code: "user:*:*:ok"}, + wantErr: true, + }, + { + name: "中间全匹配2", + fields: fields{Code: "user:*:nice:*"}, + wantErr: true, + }, + { + name: "精确权限", + fields: fields{Code: "user:1:2:3"}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Privilege{ + Code: tt.fields.Code, + } + if err := p.Validate(); (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func BenchmarkPrivilegeValidate(b *testing.B) { + ps := make([]*Privilege, 100000) + + for i := 0; i < 100000; i++ { + ps[i] = &Privilege{ + Code: tool.RandomString(4) + ":" + tool.RandomString(5) + ":" + tool.RandomString(6) + ":" + tool.RandomString(7), + } + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _ = ps[i%100000].Validate() + } +} diff --git a/model/user.go b/model/user.go index f7f00f1..08a7433 100644 --- a/model/user.go +++ b/model/user.go @@ -9,7 +9,12 @@ import ( "uauth/internal/sqlType" ) -type Status int64 +type Status string + +const ( + StatusActive Status = "active" + StatusDisabled Status = "disabled" +) type User struct { Id uint64 `json:"id" gorm:"primaryKey;column:id"` @@ -20,7 +25,7 @@ type User struct { Username string `json:"username" gorm:"column:username;type:varchar(64);unique"` Password string `json:"-" gorm:"column:password;type:varchar(256)"` - Status Status `json:"status" gorm:"column:status;default:0"` + Status Status `json:"status" gorm:"column:status;default:''"` Nickname string `json:"nickname" gorm:"column:nickname;type:varchar(64)"` Avatar string `json:"avatar" gorm:"column:avatar;type:varchar(256)"` diff --git a/rbac/rbac.go b/rbac/rbac.go index 891da58..21dc01a 100644 --- a/rbac/rbac.go +++ b/rbac/rbac.go @@ -24,6 +24,7 @@ func New(opts ...Option) (*Urbac, error) { rootPrivilege *model.Privilege rootRole *model.Role rootScope *model.Scope + rootUser *model.User ) for _, opt := range opts { @@ -60,5 +61,15 @@ func New(opts ...Option) (*Urbac, error) { } } + rootUser = &model.User{ + Username: "admin", + Password: tool.NewPassword("123456"), + Status: model.StatusActive, + Nickname: "管理员", + RoleNames: []string{"admin"}, + } + + u.newUser() + return u, nil }