2019-07-29 07:13:33 +02:00
|
|
|
package serv
|
|
|
|
|
|
|
|
import (
|
2019-09-05 06:09:56 +02:00
|
|
|
"encoding/json"
|
2019-07-29 07:13:33 +02:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2019-09-05 06:09:56 +02:00
|
|
|
const (
|
|
|
|
AL_QUERY int = iota + 1
|
|
|
|
AL_VARS
|
|
|
|
)
|
|
|
|
|
2019-07-29 07:13:33 +02:00
|
|
|
type allowItem struct {
|
2019-09-05 06:09:56 +02:00
|
|
|
uri string
|
|
|
|
gql string
|
|
|
|
vars json.RawMessage
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var _allowList allowList
|
|
|
|
|
|
|
|
type allowList struct {
|
|
|
|
list map[string]*allowItem
|
2019-08-02 16:07:50 +02:00
|
|
|
filepath string
|
2019-07-29 07:13:33 +02:00
|
|
|
saveChan chan *allowItem
|
2019-09-20 06:19:11 +02:00
|
|
|
active bool
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
|
|
|
|
2019-08-02 16:07:50 +02:00
|
|
|
func initAllowList(path string) {
|
2019-07-29 07:13:33 +02:00
|
|
|
_allowList = allowList{
|
|
|
|
list: make(map[string]*allowItem),
|
|
|
|
saveChan: make(chan *allowItem),
|
2019-09-20 06:19:11 +02:00
|
|
|
active: true,
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
2019-08-02 16:07:50 +02:00
|
|
|
|
|
|
|
if len(path) != 0 {
|
|
|
|
fp := fmt.Sprintf("%s/allow.list", path)
|
|
|
|
|
|
|
|
if _, err := os.Stat(fp); err == nil {
|
|
|
|
_allowList.filepath = fp
|
|
|
|
} else if !os.IsNotExist(err) {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(_allowList.filepath) == 0 {
|
|
|
|
fp := "./allow.list"
|
|
|
|
|
|
|
|
if _, err := os.Stat(fp); err == nil {
|
|
|
|
_allowList.filepath = fp
|
|
|
|
} else if !os.IsNotExist(err) {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(_allowList.filepath) == 0 {
|
|
|
|
fp := "./config/allow.list"
|
|
|
|
|
|
|
|
if _, err := os.Stat(fp); err == nil {
|
|
|
|
_allowList.filepath = fp
|
|
|
|
} else if !os.IsNotExist(err) {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(_allowList.filepath) == 0 {
|
|
|
|
panic("allow.list not found")
|
|
|
|
}
|
|
|
|
|
2019-07-29 07:13:33 +02:00
|
|
|
_allowList.load()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for v := range _allowList.saveChan {
|
|
|
|
_allowList.save(v)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (al *allowList) add(req *gqlReq) {
|
2019-09-20 06:19:11 +02:00
|
|
|
if al.active == false || len(req.ref) == 0 || len(req.Query) == 0 {
|
2019-07-29 07:13:33 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
al.saveChan <- &allowItem{
|
2019-09-05 06:09:56 +02:00
|
|
|
uri: req.ref,
|
|
|
|
gql: req.Query,
|
|
|
|
vars: req.Vars,
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (al *allowList) load() {
|
2019-09-20 06:19:11 +02:00
|
|
|
if al.active == false {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-08-02 16:07:50 +02:00
|
|
|
b, err := ioutil.ReadFile(al.filepath)
|
2019-07-29 07:13:33 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(b) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var uri string
|
2019-09-05 06:09:56 +02:00
|
|
|
var varBytes []byte
|
2019-07-29 07:13:33 +02:00
|
|
|
|
|
|
|
s, e, c := 0, 0, 0
|
2019-09-05 06:09:56 +02:00
|
|
|
ty := 0
|
2019-07-29 07:13:33 +02:00
|
|
|
|
|
|
|
for {
|
|
|
|
if c == 0 && b[e] == '#' {
|
|
|
|
s = e
|
2019-09-05 06:09:56 +02:00
|
|
|
for e < len(b) && b[e] != '\n' {
|
2019-07-29 07:49:48 +02:00
|
|
|
e++
|
|
|
|
}
|
|
|
|
if (e - s) > 2 {
|
|
|
|
uri = strings.TrimSpace(string(b[(s + 1):e]))
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
|
|
|
}
|
2019-09-05 06:09:56 +02:00
|
|
|
|
|
|
|
if e >= len(b) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if matchPrefix(b, e, "query") || matchPrefix(b, e, "mutation") {
|
2019-07-29 07:13:33 +02:00
|
|
|
if c == 0 {
|
|
|
|
s = e
|
|
|
|
}
|
2019-09-05 06:09:56 +02:00
|
|
|
ty = AL_QUERY
|
|
|
|
} else if matchPrefix(b, e, "variables") {
|
|
|
|
if c == 0 {
|
|
|
|
s = e + len("variables") + 1
|
|
|
|
}
|
|
|
|
ty = AL_VARS
|
|
|
|
} else if b[e] == '{' {
|
2019-07-29 07:13:33 +02:00
|
|
|
c++
|
2019-09-05 06:09:56 +02:00
|
|
|
|
2019-07-29 07:13:33 +02:00
|
|
|
} else if b[e] == '}' {
|
|
|
|
c--
|
2019-09-05 06:09:56 +02:00
|
|
|
|
2019-07-29 07:13:33 +02:00
|
|
|
if c == 0 {
|
2019-09-05 06:09:56 +02:00
|
|
|
if ty == AL_QUERY {
|
|
|
|
q := string(b[s:(e + 1)])
|
|
|
|
|
|
|
|
item := &allowItem{
|
|
|
|
uri: uri,
|
|
|
|
gql: q,
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(varBytes) != 0 {
|
|
|
|
item.vars = varBytes
|
|
|
|
}
|
|
|
|
|
|
|
|
al.list[gqlHash(q, varBytes)] = item
|
|
|
|
varBytes = nil
|
|
|
|
|
|
|
|
} else if ty == AL_VARS {
|
|
|
|
varBytes = b[s:(e + 1)]
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
2019-09-05 06:09:56 +02:00
|
|
|
ty = 0
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
e++
|
|
|
|
if e >= len(b) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (al *allowList) save(item *allowItem) {
|
2019-09-20 06:19:11 +02:00
|
|
|
if al.active == false {
|
|
|
|
return
|
|
|
|
}
|
2019-09-05 06:09:56 +02:00
|
|
|
al.list[gqlHash(item.gql, item.vars)] = item
|
2019-07-29 07:13:33 +02:00
|
|
|
|
2019-08-02 16:07:50 +02:00
|
|
|
f, err := os.Create(al.filepath)
|
2019-07-29 07:13:33 +02:00
|
|
|
if err != nil {
|
2019-08-03 17:08:16 +02:00
|
|
|
logger.Warn().Err(err).Msg("Failed to write allow list to file")
|
|
|
|
return
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
keys := []string{}
|
2019-09-05 06:09:56 +02:00
|
|
|
urlMap := make(map[string][]*allowItem)
|
2019-07-29 07:13:33 +02:00
|
|
|
|
|
|
|
for _, v := range al.list {
|
2019-09-05 06:09:56 +02:00
|
|
|
urlMap[v.uri] = append(urlMap[v.uri], v)
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for k := range urlMap {
|
|
|
|
keys = append(keys, k)
|
|
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
|
|
|
for i := range keys {
|
|
|
|
k := keys[i]
|
|
|
|
v := urlMap[k]
|
|
|
|
|
|
|
|
f.WriteString(fmt.Sprintf("# %s\n\n", k))
|
|
|
|
|
|
|
|
for i := range v {
|
2019-09-05 06:09:56 +02:00
|
|
|
if len(v[i].vars) != 0 {
|
|
|
|
vj, err := json.MarshalIndent(v[i].vars, "", "\t")
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn().Err(err).Msg("Failed to write allow list 'vars' to file")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
f.WriteString(fmt.Sprintf("variables %s\n\n", vj))
|
|
|
|
}
|
|
|
|
|
|
|
|
f.WriteString(fmt.Sprintf("%s\n\n", v[i].gql))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func matchPrefix(b []byte, i int, s string) bool {
|
|
|
|
if (len(b) - i) < len(s) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for n := 0; n < len(s); n++ {
|
|
|
|
if b[(i+n)] != s[n] {
|
|
|
|
return false
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|
|
|
|
}
|
2019-09-05 06:09:56 +02:00
|
|
|
return true
|
2019-07-29 07:13:33 +02:00
|
|
|
}
|