Compare commits

...
Sign in to create a new pull request.

8 commits

Author SHA1 Message Date
Max
80e15d5be7 [fix] CI 2023-04-13 01:37:30 +08:00
Max
868ca0132e [change] makefile 2023-04-02 10:33:47 +08:00
Max
5edc9e809f [_] fmt 2023-03-03 15:56:36 +08:00
Max
9649a1d234 [bug] fix upload file bug 2023-03-03 15:52:11 +08:00
Max
fbd3fc4c3c [add] yao.crypto.WeworkDecrypt 2023-02-09 19:19:33 +08:00
Max
b96e519981 [_] 2023-02-08 20:17:53 +08:00
Max
d7d3d832cb [change] lock tests 2023-02-08 20:10:03 +08:00
Max
3511a4bd73 [add] yao.crypto.AESBase64Encode process 2023-02-08 19:55:20 +08:00
9 changed files with 484 additions and 12 deletions

View file

@ -85,7 +85,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: ${{ env.REPO_GOU }}
ref: dc3d98411f61e21cf775e4c82d92d5ac2acfc03c
ref: v0.10.2-locked
path: gou
- name: Checkout V8Go
@ -99,6 +99,7 @@ jobs:
uses: actions/checkout@v2
with:
repository: yaoapp/yao-dev-app
ref: fef08943afc6ff91ed7a9a44559118ad40074e6b
path: app
- name: Move Kun, Xun, Gou, V8Go

View file

@ -171,10 +171,10 @@ artifacts-linux: clean
export NODE_ENV=production
rm -f ../xgen-v1.0/pnpm-lock.yaml
echo "BASE=__yao_admin_root" > ../xgen-v1.0/packages/xgen/.env
cd ../xgen-v1.0 && pnpm install && pnpm run build
cd ../xgen-v1.0 && pnpm install --no-frozen-lockfile && pnpm run build
# Setup UI
cd ../xgen-v1.0/packages/setup && pnpm install && pnpm run build
cd ../xgen-v1.0/packages/setup && pnpm install --no-frozen-lockfile && pnpm run build
# Init Application
cd ../yao-init rm -rf .git
@ -197,6 +197,7 @@ artifacts-linux: clean
sed -ie "s/const PRVERSION = \"DEV\"/const PRVERSION = \"${COMMIT}-${NOW}\"/g" share/const.go
# Making artifacts
go mod tidy
mkdir -p dist
CGO_ENABLED=1 CGO_LDFLAGS="-static" GOOS=linux GOARCH=amd64 go build -v -o dist/yao-${VERSION}-linux-amd64
CGO_ENABLED=1 CGO_LDFLAGS="-static" GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ go build -v -o dist/yao-${VERSION}-linux-arm64
@ -221,10 +222,10 @@ artifacts-macos: clean
export NODE_ENV=production
rm -f ../xgen-v1.0/pnpm-lock.yaml
echo "BASE=__yao_admin_root" > ../xgen-v1.0/packages/xgen/.env
cd ../xgen-v1.0 && pnpm install && pnpm run build
cd ../xgen-v1.0 && pnpm install --no-frozen-lockfile && pnpm run build
# Setup UI
cd ../xgen-v1.0/packages/setup && pnpm install && pnpm run build
cd ../xgen-v1.0/packages/setup && pnpm install --no-frozen-lockfile && pnpm run build
# Init Application
cd ../yao-init rm -rf .git
@ -247,6 +248,7 @@ artifacts-macos: clean
sed -ie "s/const PRVERSION = \"DEV\"/const PRVERSION = \"${COMMIT}-${NOW}\"/g" share/const.go
# Making artifacts
go mod tidy
mkdir -p dist
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -v -o dist/yao-${VERSION}-darwin-amd64
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -v -o dist/yao-${VERSION}-darwin-arm64
@ -299,10 +301,10 @@ release: clean
git clone https://github.com/YaoApp/xgen.git .tmp/xgen/v1.0
# cd .tmp/xgen/v1.0 && git checkout 5002c3fded585aaa69a4366135b415ea3234964e
echo "BASE=__yao_admin_root" > .tmp/xgen/v1.0/packages/xgen/.env
cd .tmp/xgen/v1.0 && pnpm install && pnpm run build
cd .tmp/xgen/v1.0 && pnpm install --no-frozen-lockfile && pnpm run build
# Setup UI
cd .tmp/xgen/v1.0/packages/setup && pnpm install && pnpm run build
cd .tmp/xgen/v1.0/packages/setup && pnpm install --no-frozen-lockfile && pnpm run build
# Checkout init
@ -353,10 +355,10 @@ linux-release: clean
git clone https://github.com/YaoApp/xgen.git .tmp/xgen/v1.0
rm -f .tmp/xgen/v1.0/pnpm-lock.yaml
echo "BASE=__yao_admin_root" > .tmp/xgen/v1.0/packages/xgen/.env
cd .tmp/xgen/v1.0 && pnpm install && pnpm run build
cd .tmp/xgen/v1.0 && pnpm install --no-frozen-lockfile && pnpm run build
# Setup UI
cd .tmp/xgen/v1.0/packages/setup && pnpm install && pnpm run build
cd .tmp/xgen/v1.0/packages/setup && pnpm install --no-frozen-lockfile && pnpm run build
# Checkout init

58
crypto/aes.go Normal file
View file

@ -0,0 +1,58 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
)
// Base64AESEncode AES encode
func Base64AESEncode(key []byte, text string) (string, error) {
plaintext := []byte(text)
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return "", err
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
// convert to base64
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// Base64AESDecode AES decode
func Base64AESDecode(key []byte, cryptoText string) (string, error) {
ciphertext, _ := base64.StdEncoding.DecodeString(cryptoText)
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
if len(ciphertext) < aes.BlockSize {
return "", fmt.Errorf("ciphertext too short")
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(ciphertext, ciphertext)
return fmt.Sprintf("%s", ciphertext), nil
}

37
crypto/aes_test.go Normal file
View file

@ -0,0 +1,37 @@
package crypto
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
)
func TestBase64AES(t *testing.T) {
originalText := "encrypt this golang"
key := []byte("example key 1234")
// encrypt value to base64
cryptoText, err := Base64AESEncode(key, originalText)
if err != nil {
t.Fatal(err)
}
// encrypt base64 crypto to original value
text, err := Base64AESDecode(key, cryptoText)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "encrypt this golang", text)
}
func TestBase64AESProcess(t *testing.T) {
args := []interface{}{"example key 1234", "encrypt this golang"}
cryptoText := gou.NewProcess("yao.crypto.AESBase64Encode", args...).Run()
args = []interface{}{"example key 1234", cryptoText}
text := gou.NewProcess("yao.crypto.AESBase64Decode", args...).Run()
assert.Equal(t, "encrypt this golang", text)
}

View file

@ -8,6 +8,9 @@ import (
func init() {
gou.RegisterProcessHandler("yao.crypto.hash", ProcessHash) // deprecated → crypto.Hash
gou.RegisterProcessHandler("yao.crypto.hmac", ProcessHmac) // deprecated → crypto.Hash
gou.RegisterProcessHandler("yao.crypto.AESBase64Encode", processBase64AESEncode)
gou.RegisterProcessHandler("yao.crypto.AESBase64Decode", processBase64AESDecode)
gou.RegisterProcessHandler("yao.crypto.WeworkDecrypt", processWeworkDecrypt)
gou.AliasProcess("yao.crypto.hash", "crypto.Hash")
gou.AliasProcess("yao.crypto.hmac", "crypto.Hmac")
@ -60,3 +63,44 @@ func ProcessHmac(process *gou.Process) interface{} {
}
return res
}
func processBase64AESEncode(process *gou.Process) interface{} {
process.ValidateArgNums(2)
key := process.ArgsString(0)
value := process.ArgsString(1)
res, err := Base64AESEncode([]byte(key), value)
if err != nil {
exception.New("error: %s value: %s", 400, err, value).Throw()
}
return res
}
func processBase64AESDecode(process *gou.Process) interface{} {
process.ValidateArgNums(2)
key := process.ArgsString(0)
value := process.ArgsString(1)
res, err := Base64AESDecode([]byte(key), value)
if err != nil {
exception.New("error: %s value: %s", 400, err, value).Throw()
}
return res
}
func processWeworkDecrypt(process *gou.Process) interface{} {
process.ValidateArgNums(2)
encodingAESKey := process.ArgsString(0)
msgEncrypt := process.ArgsString(1)
parseXML := false
if process.NumOfArgsIs(3) {
parseXML = process.ArgsBool(2)
}
res, err := WeworkDecrypt(encodingAESKey, msgEncrypt, parseXML)
if err != nil {
exception.New("error: %s msgEncrypt: %s", 400, err, msgEncrypt).Throw()
}
return res
}

81
crypto/wework.go Normal file
View file

@ -0,0 +1,81 @@
package crypto
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/binary"
"strings"
)
// WeworkDecrypt wework msg Decrypt
func WeworkDecrypt(encodingAESKey string, msgEncrypt string, parse bool) (map[string]interface{}, error) {
var err error
aseKey, err := base64.StdEncoding.DecodeString(encodingAESKey + "=")
if err != nil {
return nil, err
}
ciphertext, err := base64.StdEncoding.DecodeString(msgEncrypt)
if err != nil {
return nil, err
}
randMsg, err := aesDecrypt(ciphertext, aseKey)
if err != nil {
return nil, err
}
content := randMsg[16:]
buf := bytes.NewBuffer(content[0:4])
var len int32
binary.Read(buf, binary.BigEndian, &len)
msg := content[4 : len+4]
receiveid := content[len+4:]
data := map[string]interface{}{}
if parse {
data, err = parseXML(string(msg))
if err != nil {
return nil, err
}
}
return map[string]interface{}{
"message": string(msg),
"data": data,
"receiveid": string(receiveid),
}, nil
}
func parseXML(data string) (map[string]interface{}, error) {
decoder := NewDecoder(strings.NewReader(data))
result, err := decoder.Decode()
if err != nil {
return nil, err
}
return result, nil
}
func aesDecrypt(crypted, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
origData := make([]byte, len(crypted))
blockMode.CryptBlocks(origData, crypted)
origData = pckS5UnPadding(origData)
return origData, nil
}
func pckS5UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}

61
crypto/wework_test.go Normal file
View file

@ -0,0 +1,61 @@
package crypto
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/maps"
)
func TestWework(t *testing.T) {
msgEncrypt := "meqbMyPr58hNy0j0YDdG9UT60UJZSh/tb3KOZt3z2SCKr6uvmSLbEnUCM89iFXS0BLWn11FOrD/xXsGUlVUSBw=="
encodingAESKey := "RhH75tStMzrH8bMxkTw8BrBfr0ZWULL5himUaRWCs7H"
res, err := WeworkDecrypt(encodingAESKey, msgEncrypt, false)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "8446271472585838141", res["message"])
assert.Equal(t, "wwe146299c731e6301", res["receiveid"])
}
func TestWeworkProcess(t *testing.T) {
msgEncrypt := "meqbMyPr58hNy0j0YDdG9UT60UJZSh/tb3KOZt3z2SCKr6uvmSLbEnUCM89iFXS0BLWn11FOrD/xXsGUlVUSBw=="
encodingAESKey := "RhH75tStMzrH8bMxkTw8BrBfr0ZWULL5himUaRWCs7H"
args := []interface{}{encodingAESKey, msgEncrypt}
res := gou.NewProcess("yao.crypto.WeworkDecrypt", args...).Run().(map[string]interface{})
assert.Equal(t, "8446271472585838141", res["message"])
assert.Equal(t, "wwe146299c731e6301", res["receiveid"])
}
func TestWeworkParseXML(t *testing.T) {
xml := `
<xml>
<ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName>
<FromUserName><![CDATA[mycreate]]></FromUserName>
<CreateTime>1409659813</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[hello]]></Content>
<MsgId>4561255354251345929</MsgId>
<AgentID>218</AgentID>
<Nest>
<Id>111</Id>
</Nest>
</xml>`
data, err := parseXML(xml)
if err != nil {
t.Fatal(err)
}
res := maps.Of(data).Dot()
assert.Equal(t, "218", res.Get("xml.AgentID"))
assert.Equal(t, "111", res.Get("xml.Nest.Id"))
}

180
crypto/xml.go Normal file
View file

@ -0,0 +1,180 @@
package crypto
import (
"encoding/xml"
"errors"
"fmt"
"io"
"path"
"strings"
)
const (
attrPrefix = "@"
textPrefix = "#text"
)
var (
//ErrInvalidDocument invalid document err
ErrInvalidDocument = errors.New("invalid document")
//ErrInvalidRoot data at the root level is invalid err
ErrInvalidRoot = errors.New("data at the root level is invalid")
)
type node struct {
Parent *node
Value map[string]interface{}
Attrs []xml.Attr
Label string
Space string
Text string
HasMany bool
}
// Decoder instance
type Decoder struct {
r io.Reader
attrPrefix string
textPrefix string
}
// NewDecoder create new decoder instance
func NewDecoder(reader io.Reader) *Decoder {
return NewDecoderWithPrefix(reader, attrPrefix, textPrefix)
}
// NewDecoderWithPrefix create new decoder instance with custom attribute prefix and text prefix
func NewDecoderWithPrefix(reader io.Reader, attrPrefix, textPrefix string) *Decoder {
return &Decoder{r: reader, attrPrefix: attrPrefix, textPrefix: textPrefix}
}
// Decode xml string to map[string]interface{}
func (d *Decoder) Decode() (map[string]interface{}, error) {
decoder := xml.NewDecoder(d.r)
n := &node{}
stack := make([]*node, 0)
for {
token, err := decoder.Token()
if err != nil && err != io.EOF {
return nil, err
}
if token == nil {
break
}
switch tok := token.(type) {
case xml.StartElement:
{
label := tok.Name.Local
if tok.Name.Space != "" {
label = fmt.Sprintf("%s:%s", strings.ToLower(path.Base(tok.Name.Space)), tok.Name.Local)
}
n = &node{
Label: label,
Space: tok.Name.Space,
Parent: n,
Value: map[string]interface{}{label: map[string]interface{}{}},
Attrs: tok.Attr,
}
setAttrs(n, &tok, d.attrPrefix)
stack = append(stack, n)
if n.Parent != nil {
n.Parent.HasMany = true
}
}
case xml.CharData:
data := strings.TrimSpace(string(tok))
if len(stack) > 0 {
stack[len(stack)-1].Text = data
} else if len(data) > 0 {
return nil, ErrInvalidRoot
}
case xml.EndElement:
{
length := len(stack)
stack, n = stack[:length-1], stack[length-1]
if !n.HasMany {
if len(n.Attrs) > 0 {
m := n.Value[n.Label].(map[string]interface{})
m[d.textPrefix] = n.Text
} else {
n.Value[n.Label] = n.Text
}
}
if len(stack) == 0 {
return n.Value, nil
}
setNodeValue(n)
n = n.Parent
}
}
}
return nil, ErrInvalidDocument
}
func setAttrs(n *node, tok *xml.StartElement, attrPrefix string) {
if len(tok.Attr) > 0 {
m := make(map[string]interface{})
for _, attr := range tok.Attr {
if len(attr.Name.Space) > 0 {
m[attrPrefix+attr.Name.Space+":"+attr.Name.Local] = attr.Value
} else {
m[attrPrefix+attr.Name.Local] = attr.Value
}
}
n.Value[tok.Name.Local] = m
}
}
func setNodeValue(n *node) {
if v, ok := n.Parent.Value[n.Parent.Label]; ok {
m := v.(map[string]interface{})
if v, ok = m[n.Label]; ok {
switch item := v.(type) {
case string:
m[n.Label] = []string{item, n.Value[n.Label].(string)}
case []string:
m[n.Label] = append(item, n.Value[n.Label].(string))
case map[string]interface{}:
vm := getMap(n)
if vm != nil {
m[n.Label] = []map[string]interface{}{item, vm}
}
case []map[string]interface{}:
vm := getMap(n)
if vm != nil {
m[n.Label] = append(item, vm)
}
}
} else {
m[n.Label] = n.Value[n.Label]
}
} else {
n.Parent.Value[n.Parent.Label] = n.Value[n.Label]
}
}
func getMap(node *node) map[string]interface{} {
if v, ok := node.Value[node.Label]; ok {
switch v.(type) {
case string:
return map[string]interface{}{node.Label: v}
case map[string]interface{}:
return node.Value[node.Label].(map[string]interface{})
}
}
return nil
}

View file

@ -10,6 +10,7 @@ import (
"time"
"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
jsoniter "github.com/json-iterator/go"
"github.com/yaoapp/gou"
"github.com/yaoapp/gou/session"
"github.com/yaoapp/kun/any"
@ -33,9 +34,16 @@ func init() {
// processUpload 上传文件到本地服务器
func processUpload(process *gou.Process) interface{} {
process.ValidateArgNums(1)
tmpfile, ok := process.Args[0].(xun.UploadFile)
if !ok {
exception.New("上传文件参数错误", 400, process.Args[0]).Throw()
var tmpfile xun.UploadFile
data, err := jsoniter.Marshal(process.Args[0])
if err != nil {
exception.New("上传文件参数错误 %s %v", 400, err.Error(), process.Args[0]).Throw()
}
err = jsoniter.Unmarshal(data, &tmpfile)
if err != nil {
exception.New("上传文件参数错误 %s %v", 400, err.Error(), process.Args[0]).Throw()
}
hash := md5.Sum([]byte(time.Now().Format("20060102-15:04:05")))