* update

* update auth file
This commit is contained in:
joy.zhou
2019-10-30 14:44:18 +08:00
committed by GitHub
parent c7a51fe68f
commit 896769fd9d
1253 changed files with 406 additions and 481711 deletions

View File

@@ -9,7 +9,7 @@ const (
)
type Auth interface {
CheckACL(action, username, topic string) bool
CheckACL(action, clientID, username, ip, topic string) bool
CheckConnect(clientID, username, password string) bool
}

View File

@@ -0,0 +1,54 @@
## ACL Configure
```
Attention: Acl Type Change, change `pub =1, sub=2` to `sub =1, pub=2`
```
#### The ACL rules define:
~~~
Allow | type | value | pubsub | Topics
~~~
#### ACL Config
~~~
## type clientid , username, ipaddr
##sub 1 , pub 2, pubsub 3
## %c is clientid , %u is username
allow ip 127.0.0.1 2 $SYS/#
allow clientid 0001 3 #
allow username admin 3 #
allow username joy 3 /test,hello/world
allow clientid * 1 toCloud/%c
allow username * 1 toCloud/%u
deny clientid * 3 #
~~~
~~~
#allow local sub $SYS topic
allow ip 127.0.0.1 1 $SYS/#
~~~
~~~
#allow client who's id with 0001 or username with admin pub sub all topic
allow clientid 0001 3 #
allow username admin 3 #
~~~
~~~
#allow client with the username joy can pub sub topic '/test' and 'hello/world'
allow username joy 3 /test,hello/world
~~~
~~~
#allow all client pub the topic toCloud/{clientid/username}
allow clientid * 2 toCloud/%c
allow username * 2 toCloud/%u
~~~
~~~
#deny all client pub sub all topic
deny clientid * 3 #
~~~
Client match acl rule one by one
~~~
--------- --------- ---------
Client -> | Rule1 | --nomatch--> | Rule2 | --nomatch--> | Rule3 | -->
--------- --------- ---------
| | |
match match match
\|/ \|/ \|/
allow | deny allow | deny allow | deny
~~~

View File

@@ -0,0 +1,12 @@
## sub 1 , pub 2, pubsub 3
## %c is clientid , %s is username
##auth type value pub/sub topic
allow ip 127.0.0.1 2 $SYS/#
allow clientid 0001 3 #
deny username admin 3 #
allow username joy 3 /test,hello/world
allow clientid * 1 toCloud/%c
allow username * 1 toCloud/%u
allow clientid * 2 toDevice/%c
allow username * 2 toDevice/%u
deny clientid * 3 #

View File

@@ -0,0 +1,23 @@
package acl
type aclAuth struct {
config *ACLConfig
}
func Init() *aclAuth {
aclConfig, err := AclConfigLoad("./plugins/auth/authfile/acl.conf")
if err != nil {
panic(err)
}
return &aclAuth{
config: aclConfig,
}
}
func (a *aclAuth) CheckConnect(clientID, username, password string) bool {
return true
}
func (a *aclAuth) CheckACL(action, clientID, username, ip, topic string) bool {
return checkTopicAuth(a.config, action, username, ip, clientID, topic)
}

View File

@@ -0,0 +1,148 @@
package acl
import "strings"
func checkTopicAuth(ACLInfo *ACLConfig, action, ip, username, clientid, topic string) bool {
for _, info := range ACLInfo.Info {
ctyp := info.Typ
switch ctyp {
case CLIENTID:
if match, auth := info.checkWithClientID(action, clientid, topic); match {
return auth
}
case USERNAME:
if match, auth := info.checkWithUsername(action, username, topic); match {
return auth
}
case IP:
if match, auth := info.checkWithIP(action, ip, topic); match {
return auth
}
}
}
return false
}
func (a *AuthInfo) checkWithClientID(action, 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 action == PUB {
if pubTopicMatch(topic, des) {
match = true
auth = a.checkAuth(PUB)
}
} else if action == SUB {
if subTopicMatch(topic, des) {
match = true
auth = a.checkAuth(SUB)
}
}
}
}
return match, auth
}
func (a *AuthInfo) checkWithUsername(action, 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 action == PUB {
if pubTopicMatch(topic, des) {
match = true
auth = a.checkAuth(PUB)
}
} else if action == SUB {
if subTopicMatch(topic, des) {
match = true
auth = a.checkAuth(SUB)
}
}
}
}
return match, auth
}
func (a *AuthInfo) checkWithIP(action, ip, topic string) (bool, bool) {
auth := false
match := false
if a.Val == "*" || a.Val == ip {
for _, tp := range a.Topics {
des := tp
if action == PUB {
if pubTopicMatch(topic, des) {
auth = a.checkAuth(PUB)
match = true
}
} else if action == SUB {
if subTopicMatch(topic, des) {
auth = a.checkAuth(SUB)
match = true
}
}
}
}
return match, auth
}
func (a *AuthInfo) checkAuth(action string) bool {
auth := false
if action == 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 action == 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"
"strings"
)
const (
SUB = "1"
PUB = "2"
PUBSUB = "3"
CLIENTID = "clientid"
USERNAME = "username"
IP = "ip"
ALLOW = "allow"
DENY = "deny"
)
type AuthInfo struct {
Auth string
Typ string
Val string
PubSub string
Topics []string
}
type ACLConfig struct {
File string
Info []*AuthInfo
}
func AclConfigLoad(file string) (*ACLConfig, error) {
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
}
if tmpArr[3] != PUB && tmpArr[3] != SUB && tmpArr[3] != PUBSUB {
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: tmpArr[3],
}
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
}
}

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
}

View File

@@ -138,7 +138,7 @@ func (a *authHTTP) CheckConnect(clientID, username, password string) bool {
// }
//CheckACL check mqtt connect
func (a *authHTTP) CheckACL(action, username, topic string) bool {
func (a *authHTTP) CheckACL(action, clientID, username, ip, topic string) bool {
{
aCache := checkCache(action, "", username, "", topic)

View File

@@ -2,7 +2,7 @@ package auth
type mockAuth struct{}
func (m *mockAuth) CheckACL(action, username, topic string) bool {
func (m *mockAuth) CheckACL(action, clientID, username, ip, topic string) bool {
return true
}