mirror of
https://github.com/fhmq/hmq.git
synced 2026-04-24 10:38:34 +00:00
update
This commit is contained in:
28
plugins/auth/acl/acl.go
Normal file
28
plugins/auth/acl/acl.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package acl
|
||||||
|
|
||||||
|
type aclAuth struct {
|
||||||
|
config *ACLConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() *aclAuth {
|
||||||
|
aclConfig, err := AclConfigLoad("./plugins/auth/authhttp/http.json")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &aclAuth{
|
||||||
|
config: aclConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *aclAuth) CheckConnect(clientID, username, password string) bool {
|
||||||
|
return checkTopicAuth(a.config, clientID, username, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *aclAuth) CheckACL(action, username, topic string) bool {
|
||||||
|
return checkTopicAuth(a.config, action, clientID, username, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// type Auth interface {
|
||||||
|
// CheckACL(action, username, topic string) bool
|
||||||
|
// CheckConnect(clientID, username, password string) bool
|
||||||
|
// }
|
||||||
148
plugins/auth/acl/aclcheck.go
Normal file
148
plugins/auth/acl/aclcheck.go
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
package acl
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func checkTopicAuth(ACLInfo *ACLConfig, typ int, ip, username, clientid, topic string) bool {
|
||||||
|
for _, info := range ACLInfo.Info {
|
||||||
|
ctyp := info.Typ
|
||||||
|
switch ctyp {
|
||||||
|
case CLIENTID:
|
||||||
|
if match, auth := info.checkWithClientID(typ, clientid, topic); match {
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
case USERNAME:
|
||||||
|
if match, auth := info.checkWithUsername(typ, username, topic); match {
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
case IP:
|
||||||
|
if match, auth := info.checkWithIP(typ, ip, topic); match {
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthInfo) checkWithClientID(typ int, clientid, topic string) (bool, bool) {
|
||||||
|
auth := false
|
||||||
|
match := false
|
||||||
|
if a.Val == "*" || a.Val == clientid {
|
||||||
|
for _, tp := range a.Topics {
|
||||||
|
des := strings.Replace(tp, "%c", clientid, -1)
|
||||||
|
if typ == PUB {
|
||||||
|
if pubTopicMatch(topic, des) {
|
||||||
|
match = true
|
||||||
|
auth = a.checkAuth(PUB)
|
||||||
|
}
|
||||||
|
} else if typ == SUB {
|
||||||
|
if subTopicMatch(topic, des) {
|
||||||
|
match = true
|
||||||
|
auth = a.checkAuth(SUB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match, auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthInfo) checkWithUsername(typ int, username, topic string) (bool, bool) {
|
||||||
|
auth := false
|
||||||
|
match := false
|
||||||
|
if a.Val == "*" || a.Val == username {
|
||||||
|
for _, tp := range a.Topics {
|
||||||
|
des := strings.Replace(tp, "%u", username, -1)
|
||||||
|
if typ == PUB {
|
||||||
|
if pubTopicMatch(topic, des) {
|
||||||
|
match = true
|
||||||
|
auth = a.checkAuth(PUB)
|
||||||
|
}
|
||||||
|
} else if typ == SUB {
|
||||||
|
if subTopicMatch(topic, des) {
|
||||||
|
match = true
|
||||||
|
auth = a.checkAuth(SUB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match, auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthInfo) checkWithIP(typ int, ip, topic string) (bool, bool) {
|
||||||
|
auth := false
|
||||||
|
match := false
|
||||||
|
if a.Val == "*" || a.Val == ip {
|
||||||
|
for _, tp := range a.Topics {
|
||||||
|
des := tp
|
||||||
|
if typ == PUB {
|
||||||
|
if pubTopicMatch(topic, des) {
|
||||||
|
auth = a.checkAuth(PUB)
|
||||||
|
match = true
|
||||||
|
}
|
||||||
|
} else if typ == SUB {
|
||||||
|
if subTopicMatch(topic, des) {
|
||||||
|
auth = a.checkAuth(SUB)
|
||||||
|
match = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match, auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AuthInfo) checkAuth(typ int) bool {
|
||||||
|
auth := false
|
||||||
|
if typ == PUB {
|
||||||
|
if a.Auth == ALLOW && (a.PubSub == PUB || a.PubSub == PUBSUB) {
|
||||||
|
auth = true
|
||||||
|
} else if a.Auth == DENY && a.PubSub == SUB {
|
||||||
|
auth = true
|
||||||
|
}
|
||||||
|
} else if typ == SUB {
|
||||||
|
if a.Auth == ALLOW && (a.PubSub == SUB || a.PubSub == PUBSUB) {
|
||||||
|
auth = true
|
||||||
|
} else if a.Auth == DENY && a.PubSub == PUB {
|
||||||
|
auth = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func pubTopicMatch(pub, des string) bool {
|
||||||
|
dest, _ := SubscribeTopicSpilt(des)
|
||||||
|
topic, _ := PublishTopicSpilt(pub)
|
||||||
|
for i, t := range dest {
|
||||||
|
if i > len(topic)-1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t == "#" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if t == "+" || t == topic[i] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if t != topic[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func subTopicMatch(pub, des string) bool {
|
||||||
|
dest, _ := SubscribeTopicSpilt(des)
|
||||||
|
topic, _ := SubscribeTopicSpilt(pub)
|
||||||
|
for i, t := range dest {
|
||||||
|
if i > len(topic)-1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t == "#" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if t == "+" || "+" == topic[i] || t == topic[i] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if t != topic[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
114
plugins/auth/acl/aclconfig.go
Normal file
114
plugins/auth/acl/aclconfig.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PUB = 1
|
||||||
|
SUB = 2
|
||||||
|
PUBSUB = 3
|
||||||
|
CLIENTID = "clientid"
|
||||||
|
USERNAME = "username"
|
||||||
|
IP = "ip"
|
||||||
|
ALLOW = "allow"
|
||||||
|
DENY = "deny"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthInfo struct {
|
||||||
|
Auth string
|
||||||
|
Typ string
|
||||||
|
Val string
|
||||||
|
PubSub int
|
||||||
|
Topics []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ACLConfig struct {
|
||||||
|
File string
|
||||||
|
Info []*AuthInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func AclConfigLoad(file string) (*ACLConfig, error) {
|
||||||
|
if file == "" {
|
||||||
|
file = "./conf/acl.conf"
|
||||||
|
}
|
||||||
|
aclconifg := &ACLConfig{
|
||||||
|
File: file,
|
||||||
|
Info: make([]*AuthInfo, 0, 4),
|
||||||
|
}
|
||||||
|
err := aclconifg.Prase()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return aclconifg, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ACLConfig) Prase() error {
|
||||||
|
f, err := os.Open(c.File)
|
||||||
|
defer f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf := bufio.NewReader(f)
|
||||||
|
var parseErr error
|
||||||
|
for {
|
||||||
|
line, err := buf.ReadString('\n')
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if isCommentOut(line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if line == "" {
|
||||||
|
return parseErr
|
||||||
|
}
|
||||||
|
// fmt.Println(line)
|
||||||
|
tmpArr := strings.Fields(line)
|
||||||
|
if len(tmpArr) != 5 {
|
||||||
|
parseErr = errors.New("\"" + line + "\" format is error")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if tmpArr[0] != ALLOW && tmpArr[0] != DENY {
|
||||||
|
parseErr = errors.New("\"" + line + "\" format is error")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if tmpArr[1] != CLIENTID && tmpArr[1] != USERNAME && tmpArr[1] != IP {
|
||||||
|
parseErr = errors.New("\"" + line + "\" format is error")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var pubsub int
|
||||||
|
pubsub, err = strconv.Atoi(tmpArr[3])
|
||||||
|
if err != nil {
|
||||||
|
parseErr = errors.New("\"" + line + "\" format is error")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
topicStr := strings.Replace(tmpArr[4], " ", "", -1)
|
||||||
|
topicStr = strings.Replace(topicStr, "\n", "", -1)
|
||||||
|
topics := strings.Split(topicStr, ",")
|
||||||
|
tmpAuth := &AuthInfo{
|
||||||
|
Auth: tmpArr[0],
|
||||||
|
Typ: tmpArr[1],
|
||||||
|
Val: tmpArr[2],
|
||||||
|
Topics: topics,
|
||||||
|
PubSub: pubsub,
|
||||||
|
}
|
||||||
|
c.Info = append(c.Info, tmpAuth)
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
parseErr = err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parseErr
|
||||||
|
}
|
||||||
|
func isCommentOut(line string) bool {
|
||||||
|
if strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") || strings.HasPrefix(line, "//") || strings.HasPrefix(line, "*") {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
52
plugins/auth/acl/spilt.go
Normal file
52
plugins/auth/acl/spilt.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SubscribeTopicSpilt(topic string) ([]string, error) {
|
||||||
|
subject := []byte(topic)
|
||||||
|
if bytes.IndexByte(subject, '#') != -1 {
|
||||||
|
if bytes.IndexByte(subject, '#') != len(subject)-1 {
|
||||||
|
return nil, errors.New("Topic format error with index of #")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
re := strings.Split(topic, "/")
|
||||||
|
for i, v := range re {
|
||||||
|
if i != 0 && i != (len(re)-1) {
|
||||||
|
if v == "" {
|
||||||
|
return nil, errors.New("Topic format error with index of //")
|
||||||
|
}
|
||||||
|
if strings.Contains(v, "+") && v != "+" {
|
||||||
|
return nil, errors.New("Topic format error with index of +")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if v == "" {
|
||||||
|
re[i] = "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return re, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func PublishTopicSpilt(topic string) ([]string, error) {
|
||||||
|
subject := []byte(topic)
|
||||||
|
if bytes.IndexByte(subject, '#') != -1 || bytes.IndexByte(subject, '+') != -1 {
|
||||||
|
return nil, errors.New("Publish Topic format error with + and #")
|
||||||
|
}
|
||||||
|
re := strings.Split(topic, "/")
|
||||||
|
for i, v := range re {
|
||||||
|
if v == "" {
|
||||||
|
if i != 0 && i != (len(re)-1) {
|
||||||
|
return nil, errors.New("Topic format error with index of //")
|
||||||
|
} else {
|
||||||
|
re[i] = "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return re, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user