Skip to content

Commit 0a92b89

Browse files
committed
feat: add enforcer
1 parent 55c1fb6 commit 0a92b89

File tree

6 files changed

+151
-14
lines changed

6 files changed

+151
-14
lines changed

cmd/password/password.go

+14-8
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,30 @@ package main
33
import (
44
"crypto/md5"
55
"fmt"
6-
"github.com/housepower/ckman/common"
7-
"golang.org/x/term"
86
"os"
97
"path"
108
"syscall"
11-
)
129

10+
"github.com/housepower/ckman/common"
11+
"golang.org/x/term"
12+
)
1313

1414
func main() {
15-
fmt.Printf("Initiating the setup of password for reserved user %s\n", common.DefaultUserName)
1615
fmt.Println(`Password must be at least 8 characters long.
1716
Password must contain at least three character categories among the following:
1817
* Uppercase characters (A-Z)
1918
* Lowercase characters (a-z)
2019
* Digits (0-9)
2120
* Special characters (~!@#$%^&*_-+=|\(){}[]:;"'<>,.?/)`)
2221

23-
fmt.Printf("\nEnter password for [%s]: ", common.DefaultUserName)
22+
fmt.Printf("\nEnter username(ckman/guest):")
23+
var username string
24+
fmt.Scanf("%s", &username)
25+
if !common.UsernameInvalid(username) {
26+
fmt.Printf("invalid username, expect %s or %s\n", common.DefaultAdminName, common.DefaultGuestName)
27+
return
28+
}
29+
fmt.Printf("\nEnter password for [%s]: ", username)
2430
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
2531
if err != nil {
2632
fmt.Printf("\nEnter password fail: %v\n", err)
@@ -33,7 +39,7 @@ Password must contain at least three character categories among the following:
3339
return
3440
}
3541

36-
fmt.Printf("\nReenter password for [%s]: ", common.DefaultUserName)
42+
fmt.Printf("\nReenter password for [%s]: ", username)
3743
dupPassword, err := term.ReadPassword(int(syscall.Stdin))
3844
if err != nil {
3945
fmt.Printf("\nReenter password fail: %v\n", err)
@@ -52,7 +58,7 @@ Password must contain at least three character categories among the following:
5258
return
5359
}
5460

55-
passwordFile := path.Join(common.GetWorkDirectory(), "conf/password")
61+
passwordFile := path.Join(common.GetWorkDirectory(), path.Join("conf", common.PasswordFile[username]))
5662
fileFd, err := os.OpenFile(passwordFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
5763
if err != nil {
5864
fmt.Printf("\nOpen password file %s fail: %v\n", passwordFile, err)
@@ -65,5 +71,5 @@ Password must contain at least three character categories among the following:
6571
return
6672
}
6773

68-
fmt.Printf("\nSet password for [%s] success\n", common.DefaultUserName)
74+
fmt.Printf("\nSet password for [%s] success\n", username)
6975
}

common/jwt.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,20 @@ import (
66
)
77

88
var (
9-
DefaultUserName = "ckman"
9+
DefaultAdminName = "ckman"
10+
DefaultGuestName = "guest"
1011
DefaultSigningKey = "change me"
1112
)
1213

14+
var PasswordFile = map[string]string{
15+
DefaultAdminName: "password",
16+
DefaultGuestName: "guestpassword",
17+
}
18+
19+
func UsernameInvalid(username string) bool {
20+
return ArraySearch(username, []string{DefaultAdminName, DefaultGuestName})
21+
}
22+
1323
type JWT struct {
1424
SigningKey []byte
1525
}

common/jwt_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func TestJwt(t *testing.T) {
1414
StandardClaims: jwt.StandardClaims{
1515
IssuedAt: 1136160245,
1616
},
17-
Name: DefaultUserName,
17+
Name: DefaultAdminName,
1818
ClientIP: "172.16.144.1",
1919
}
2020
token, err := j.CreateToken(claims)

controller/user.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ func (controller *UserController) Login(c *gin.Context) {
4747
return
4848
}
4949

50-
if req.Username != common.DefaultUserName {
50+
if !common.UsernameInvalid(req.Username) {
5151
controller.wrapfunc(c, model.E_USER_VERIFY_FAIL, nil)
5252
return
5353
}
5454

55-
passwordFile := path.Join(filepath.Dir(controller.config.ConfigFile), "password")
55+
passwordFile := path.Join(filepath.Dir(controller.config.ConfigFile), common.PasswordFile[req.Username])
5656
data, err := os.ReadFile(passwordFile)
5757
if err != nil {
5858
controller.wrapfunc(c, model.E_GET_USER_PASSWORD_FAIL, err)
@@ -70,7 +70,7 @@ func (controller *UserController) Login(c *gin.Context) {
7070
IssuedAt: time.Now().Unix(),
7171
// ExpiresAt: time.Now().Add(time.Second * time.Duration(d.config.Server.SessionTimeout)).Unix(),
7272
},
73-
Name: common.DefaultUserName,
73+
Name: req.Username,
7474
ClientIP: c.ClientIP(),
7575
}
7676
token, err := j.CreateToken(claims)

server/enforce/enforce.go

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package enforce
2+
3+
import (
4+
"strings"
5+
)
6+
7+
const (
8+
ADMIN string = "ckman"
9+
GUEST string = "guest"
10+
11+
POST string = "POST"
12+
GET string = "GET"
13+
PUT string = "PUT"
14+
DELETE string = "DELETE"
15+
)
16+
17+
type Policy struct {
18+
User string
19+
URL string
20+
Method string
21+
}
22+
23+
type Model struct {
24+
Admin string
25+
UrlPrefix []string
26+
}
27+
28+
type Enforcer struct {
29+
model Model
30+
policies []Policy
31+
}
32+
33+
var DefaultModel = Model{
34+
Admin: ADMIN,
35+
UrlPrefix: []string{
36+
"/api/v1", "/api/v2",
37+
},
38+
}
39+
var e *Enforcer
40+
41+
func init() {
42+
e = &Enforcer{
43+
model: DefaultModel,
44+
policies: []Policy{
45+
{GUEST, "/ck/cluster", GET},
46+
{GUEST, "/ck/cluster/*", GET},
47+
{GUEST, "/ck/table/*", GET},
48+
{GUEST, "/ck/table/group_uniq_array/*", GET},
49+
{GUEST, "/ck/query/*", GET},
50+
{GUEST, "/ck/query_explain/*", GET},
51+
{GUEST, "/ck/query_history/*", GET},
52+
{GUEST, "/ck/table_lists/*", GET},
53+
{GUEST, "/ck/table_schema/*", GET},
54+
{GUEST, "/ck/get/*", GET},
55+
{GUEST, "/ck/partition/*", GET},
56+
{GUEST, "/ck/table_metric/*", GET},
57+
{GUEST, "/ck/table_merges/*", GET},
58+
{GUEST, "/ck/open_sessions/*", GET},
59+
{GUEST, "/ck/slow_sessions/*", GET},
60+
{GUEST, "/ck/ddl_queue/*", GET},
61+
{GUEST, "/ck/node/log/*", POST},
62+
{GUEST, "/ck/ping/*", POST},
63+
{GUEST, "/ck/config/*", GET},
64+
{GUEST, "/zk/status/*", GET},
65+
{GUEST, "/zk/replicated_table/*", GET},
66+
{GUEST, "/package", GET},
67+
{GUEST, "/metric/query/*", GET},
68+
{GUEST, "/metric/query_range/*", GET},
69+
{GUEST, "/version", GET},
70+
{GUEST, "/ui/schema", GET},
71+
{GUEST, "/task/*", GET},
72+
{GUEST, "/task/lists", GET},
73+
{GUEST, "/task/running", GET},
74+
},
75+
}
76+
}
77+
78+
func (e *Enforcer) Match(url1, url2 string) bool {
79+
for _, prefix := range e.model.UrlPrefix {
80+
if !strings.HasPrefix(url2, prefix) {
81+
return false
82+
}
83+
url22 := strings.TrimPrefix(url2, prefix)
84+
url11 := strings.TrimSuffix(url1, "*")
85+
if strings.HasPrefix(url22, url11) {
86+
return true
87+
}
88+
}
89+
90+
return false
91+
}
92+
93+
func Enforce(username, url, method string) bool {
94+
if username == e.model.Admin {
95+
return true
96+
}
97+
98+
for _, policy := range e.policies {
99+
if policy.User == username && e.Match(policy.URL, url) && policy.Method == method {
100+
return true
101+
}
102+
}
103+
return false
104+
}

server/server.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/housepower/ckman/model"
2727
"github.com/housepower/ckman/repository"
2828
"github.com/housepower/ckman/router"
29+
"github.com/housepower/ckman/server/enforce"
2930
"github.com/housepower/ckman/service/prometheus"
3031
ginSwagger "github.com/swaggo/gin-swagger"
3132
"github.com/swaggo/gin-swagger/swaggerFiles"
@@ -99,6 +100,7 @@ func (server *ApiServer) Start() error {
99100
// add authenticate middleware for /api
100101
groupApi.Use(ginJWTAuth())
101102
groupApi.Use(ginRefreshTokenExpires())
103+
groupApi.Use(ginEnforce())
102104
groupApi.PUT("/logout", userController.Logout)
103105
groupV1 := groupApi.Group("/v1")
104106
router.InitRouterV1(groupV1, server.config, server.signal)
@@ -206,6 +208,8 @@ func ginJWTAuth() gin.HandlerFunc {
206208
c.Abort()
207209
return
208210
}
211+
//c.Set("username", userToken.UserId)
212+
c.Set("username", common.DefaultAdminName)
209213
return
210214
}
211215

@@ -257,8 +261,8 @@ func ginJWTAuth() gin.HandlerFunc {
257261
if clientIp == claims.ClientIP {
258262
c.Set("claims", claims)
259263
c.Set("token", token)
264+
c.Set("username", claims.Name)
260265
return
261-
262266
}
263267
}
264268
if claims.ClientIP != c.ClientIP() {
@@ -270,6 +274,7 @@ func ginJWTAuth() gin.HandlerFunc {
270274

271275
c.Set("claims", claims)
272276
c.Set("token", token)
277+
c.Set("username", claims.Name)
273278
}
274279
}
275280

@@ -313,3 +318,15 @@ func PromHttpSD(c *gin.Context) {
313318
objs := prometheus.GetObjects(clusters)
314319
c.JSON(http.StatusOK, objs[schema])
315320
}
321+
322+
func ginEnforce() gin.HandlerFunc {
323+
return func(c *gin.Context) {
324+
username := c.GetString("username")
325+
ok := enforce.Enforce(username, c.Request.URL.RequestURI(), c.Request.Method)
326+
if !ok {
327+
err := fmt.Errorf("permission denied: username [%s]", username)
328+
router.WrapMsg(c, model.E_USER_VERIFY_FAIL, err)
329+
c.Abort()
330+
}
331+
}
332+
}

0 commit comments

Comments
 (0)