Skip to content

Commit

Permalink
feat: user privilege management
Browse files Browse the repository at this point in the history
  • Loading branch information
YenchangChan committed Jun 4, 2024
1 parent 13cc665 commit 8c5d36c
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 73 deletions.
16 changes: 8 additions & 8 deletions cmd/password/password.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ import (
)

func main() {
common.LoadUsers(path.Join(common.GetWorkDirectory(), "conf"))
fmt.Println(`Password must be at least 8 characters long.
Password must contain at least three character categories among the following:
* Uppercase characters (A-Z)
* Lowercase characters (a-z)
* Digits (0-9)
* Special characters (~!@#$%^&*_-+=|\(){}[]:;"'<>,.?/)`)

fmt.Printf("\nEnter username(ckman/guest):")
fmt.Printf("\nEnter username:")
var username string
fmt.Scanf("%s", &username)
if !common.UsernameInvalid(username) {
fmt.Printf("invalid username, expect %s or %s\n", common.DefaultAdminName, common.DefaultGuestName)
userinfo, err := common.GetUserInfo(username)
if err != nil {
fmt.Printf("\nGet user info fail: %v\n", err)
return
}
fmt.Printf("\nEnter password for [%s]: ", username)
Expand Down Expand Up @@ -58,16 +60,14 @@ Password must contain at least three character categories among the following:
return
}

passwordFile := path.Join(common.GetWorkDirectory(), path.Join("conf", common.PasswordFile[username]))
fileFd, err := os.OpenFile(passwordFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
fileFd, err := os.OpenFile(userinfo.UserFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
fmt.Printf("\nOpen password file %s fail: %v\n", passwordFile, err)
fmt.Printf("\nOpen password file %s fail: %v\n", userinfo.UserFile, err)
return
}
defer fileFd.Close()

if _, err := fileFd.Write([]byte(hash)); err != nil {
fmt.Printf("\nWrite password file %s fail: %v\n", passwordFile, err)
fmt.Printf("\nWrite password file %s fail: %v\n", userinfo.UserFile, err)
return
}

Expand Down
11 changes: 0 additions & 11 deletions common/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,9 @@ import (
)

var (
DefaultAdminName = "ckman"
DefaultGuestName = "guest"
DefaultSigningKey = "change me"
)

var PasswordFile = map[string]string{
DefaultAdminName: "password",
DefaultGuestName: "guestpassword",
}

func UsernameInvalid(username string) bool {
return ArraySearch(username, []string{DefaultAdminName, DefaultGuestName})
}

type JWT struct {
SigningKey []byte
}
Expand Down
88 changes: 88 additions & 0 deletions common/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package common

import (
"fmt"
"os"
"path"
"strings"
"sync"
)

const (
ADMIN string = "ckman"
GUEST string = "guest"
ORDINARY string = "ordinary"

DefaultAdminName = "ckman"
InternalOrdinaryName = "ordinary"
)

type UserInfo struct {
Policy string
Password string
UserFile string
}

var UserMap map[string]UserInfo
var lock sync.Mutex

func LoadUsers(configPath string) {
//usermap
lock.Lock()
defer lock.Unlock()
UserMap = make(map[string]UserInfo)
userPath := path.Join(configPath, "users")
entries, err := os.ReadDir(userPath)
if err == nil {
for _, entry := range entries {
if entry.IsDir() {
continue
}

userfile := path.Join(userPath, entry.Name())
userinfos := strings.Split(entry.Name(), ".")
if len(userinfos) != 2 {
continue
}
username := userinfos[0]
policy := userinfos[1]
if policy != GUEST && policy != ORDINARY {
continue
}
password, err := os.ReadFile(userfile)
if err == nil {
UserMap[username] = UserInfo{
Policy: policy,
Password: string(password),
UserFile: userfile,
}
}
}
}

passwordFile := path.Join(configPath, "password")
password, err := os.ReadFile(passwordFile)
if err != nil {
return
}

UserMap[DefaultAdminName] = UserInfo{
Policy: ADMIN,
Password: string(password),
UserFile: passwordFile,
}

UserMap[InternalOrdinaryName] = UserInfo{
Policy: ORDINARY,
//Password: "change me", // InternalOrdinaryName 专门给userToken方式的用户使用,无需密码
}
}

func GetUserInfo(name string) (UserInfo, error) {
lock.Lock()
defer lock.Unlock()
if userinfo, ok := UserMap[name]; ok {
return userinfo, nil
}
return UserInfo{}, fmt.Errorf("user %s doesn't exist", name)
}
16 changes: 4 additions & 12 deletions controller/user.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package controller

import (
"os"
"path"
"path/filepath"
"time"

Expand Down Expand Up @@ -42,24 +40,18 @@ func NewUserController(config *config.CKManConfig, wrapfunc Wrapfunc) *UserContr
func (controller *UserController) Login(c *gin.Context) {
var req model.LoginReq
c.Request.Header.Get("")
common.LoadUsers(filepath.Dir(controller.config.ConfigFile))
if err := model.DecodeRequestBody(c.Request, &req); err != nil {
controller.wrapfunc(c, model.E_INVALID_PARAMS, err)
return
}

if !common.UsernameInvalid(req.Username) {
controller.wrapfunc(c, model.E_USER_VERIFY_FAIL, nil)
return
}

passwordFile := path.Join(filepath.Dir(controller.config.ConfigFile), common.PasswordFile[req.Username])
data, err := os.ReadFile(passwordFile)
userinfo, err := common.GetUserInfo(req.Username)
if err != nil {
controller.wrapfunc(c, model.E_GET_USER_PASSWORD_FAIL, err)
controller.wrapfunc(c, model.E_USER_VERIFY_FAIL, err)
return
}

if pass := common.ComparePassword(string(data), req.Password); !pass {
if pass := common.ComparePassword(userinfo.Password, req.Password); !pass {
controller.wrapfunc(c, model.E_PASSWORD_VERIFY_FAIL, nil)
return
}
Expand Down
64 changes: 23 additions & 41 deletions server/enforce/enforce.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@ package enforce

import (
"strings"

"github.com/housepower/ckman/common"
)

const (
ADMIN string = "ckman"
GUEST string = "guest"

POST string = "POST"
GET string = "GET"
PUT string = "PUT"
DELETE string = "DELETE"
)

type Policy struct {
User string
URL string
Method string
}
Expand All @@ -26,12 +24,13 @@ type Model struct {
}

type Enforcer struct {
model Model
policies []Policy
model Model
guest []Policy
orinary []Policy
}

var DefaultModel = Model{
Admin: ADMIN,
Admin: common.ADMIN,
UrlPrefix: []string{
"/api/v1", "/api/v2",
},
Expand All @@ -40,38 +39,9 @@ var e *Enforcer

func init() {
e = &Enforcer{
model: DefaultModel,
policies: []Policy{
{GUEST, "/ck/cluster", GET},
{GUEST, "/ck/cluster/*", GET},
{GUEST, "/ck/table/*", GET},
{GUEST, "/ck/table/group_uniq_array/*", GET},
{GUEST, "/ck/query/*", GET},
{GUEST, "/ck/query_explain/*", GET},
{GUEST, "/ck/query_history/*", GET},
{GUEST, "/ck/table_lists/*", GET},
{GUEST, "/ck/table_schema/*", GET},
{GUEST, "/ck/get/*", GET},
{GUEST, "/ck/partition/*", GET},
{GUEST, "/ck/table_metric/*", GET},
{GUEST, "/ck/table_merges/*", GET},
{GUEST, "/ck/open_sessions/*", GET},
{GUEST, "/ck/slow_sessions/*", GET},
{GUEST, "/ck/ddl_queue/*", GET},
{GUEST, "/ck/node/log/*", POST},
{GUEST, "/ck/ping/*", POST},
{GUEST, "/ck/config/*", GET},
{GUEST, "/zk/status/*", GET},
{GUEST, "/zk/replicated_table/*", GET},
{GUEST, "/package", GET},
{GUEST, "/metric/query/*", GET},
{GUEST, "/metric/query_range/*", GET},
{GUEST, "/version", GET},
{GUEST, "/ui/schema", GET},
{GUEST, "/task/*", GET},
{GUEST, "/task/lists", GET},
{GUEST, "/task/running", GET},
},
model: DefaultModel,
guest: GuestPolicies(),
orinary: OrdinaryPolicies(),
}
}

Expand All @@ -95,8 +65,20 @@ func Enforce(username, url, method string) bool {
return true
}

for _, policy := range e.policies {
if policy.User == username && e.Match(policy.URL, url) && policy.Method == method {
userinfo, err := common.GetUserInfo(username)
if err != nil {
return false
}
var policies []Policy
switch userinfo.Policy {
case common.GUEST:
policies = e.guest
case common.ORDINARY:
policies = e.orinary
}

for _, policy := range policies {
if e.Match(policy.URL, url) && policy.Method == method {
return true
}
}
Expand Down
35 changes: 35 additions & 0 deletions server/enforce/guest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package enforce

func GuestPolicies() []Policy {
return []Policy{
{"/ck/cluster", GET},
{"/ck/cluster/*", GET},
{"/ck/table/*", GET},
{"/ck/table/group_uniq_array/*", GET},
{"/ck/query/*", GET},
{"/ck/query_explain/*", GET},
{"/ck/query_history/*", GET},
{"/ck/table_lists/*", GET},
{"/ck/table_schema/*", GET},
{"/ck/get/*", GET},
{"/ck/partition/*", GET},
{"/ck/table_metric/*", GET},
{"/ck/table_merges/*", GET},
{"/ck/open_sessions/*", GET},
{"/ck/slow_sessions/*", GET},
{"/ck/ddl_queue/*", GET},
{"/ck/node/log/*", POST},
{"/ck/ping/*", POST},
{"/ck/config/*", GET},
{"/zk/status/*", GET},
{"/zk/replicated_table/*", GET},
{"/package", GET},
{"/metric/query/*", GET},
{"/metric/query_range/*", GET},
{"/version", GET},
{"/ui/schema", GET},
{"/task/*", GET},
{"/task/lists", GET},
{"/task/running", GET},
}
}
24 changes: 24 additions & 0 deletions server/enforce/ordinary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package enforce

func OrdinaryPolicies() []Policy {
var OrdinaryPolicies = []Policy{
{"/ck/table/*", POST},
{"/ck/dist_logic_table/*", POST},
{"/ck/dist_logic_table/*", DELETE},
{"/ck/table/*", PUT},
{"/ck/truncate_table/*", DELETE},
{"/ck/table/ttl/*", PUT},
{"/ck/table/readonly/*", PUT},
{"/ck/table/view/*", PUT},
{"/ck/table/orderby/*", PUT},
{"/ck/table/group_uniq_array/*", POST},
{"/ck/table/group_uniq_array/*", DELETE},
{"/ck/table/*", DELETE},
{"/ck/table_all/*", DELETE},
{"/ck/query_history/*", DELETE},
{"/ck/open_sessions/*", PUT},
{"/ck/purge_tables/*", POST},
{"/ck/archive/*", POST},
}
return append(OrdinaryPolicies, GuestPolicies()...)
}
2 changes: 1 addition & 1 deletion server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ func ginJWTAuth() gin.HandlerFunc {
return
}
//c.Set("username", userToken.UserId)
c.Set("username", common.DefaultAdminName)
c.Set("username", common.InternalOrdinaryName)
return
}

Expand Down

0 comments on commit 8c5d36c

Please sign in to comment.