This commit is contained in:
joyz
2019-10-27 08:58:41 +08:00
parent c7a51fe68f
commit a02007ce6b
4 changed files with 342 additions and 0 deletions

28
plugins/auth/acl/acl.go Normal file
View 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
// }

View 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
}

View 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
View 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
}