1
0
forked from x/ContextOS
2018-06-25 22:40:43 +08:00

394 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package aaa // {{{
// }}}
import ( // {{{
"contexts"
"io"
"io/ioutil"
"os"
"crypto"
"crypto/md5"
crand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"encoding/hex"
"math/rand"
"fmt"
"strconv"
"time"
)
// }}}
type AAA struct {
share map[string]*ctx.Context
sessions map[string]*ctx.Context
*ctx.Context
}
func (aaa *AAA) Session(meta string) string { // {{{
bs := md5.Sum([]byte(fmt.Sprintln("%d%d%s", time.Now().Unix(), rand.Int(), meta)))
sessid := hex.EncodeToString(bs[:])
return sessid
}
// }}}
func (aaa *AAA) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server { // {{{
c.Caches = map[string]*ctx.Cache{}
c.Configs = map[string]*ctx.Config{}
c.Index = map[string]*ctx.Context{
"void": &ctx.Context{Name: "void", Help: "void",
Caches: map[string]*ctx.Cache{"group": &ctx.Cache{}},
Configs: map[string]*ctx.Config{"rootname": &ctx.Config{}},
Commands: map[string]*ctx.Command{"login": &ctx.Command{}},
},
}
s := new(AAA)
s.Context = c
return s
}
// }}}
func (aaa *AAA) Begin(m *ctx.Message, arg ...string) ctx.Server { // {{{
aaa.Context.Master(nil)
aaa.Caches["group"] = &ctx.Cache{Name: "用户组", Value: "", Help: "用户组"}
aaa.Caches["username"] = &ctx.Cache{Name: "用户名", Value: "", Help: "用户名"}
aaa.Caches["password"] = &ctx.Cache{Name: "用户密码", Value: "", Help: "用户密码,加密存储", Hand: func(m *ctx.Message, x *ctx.Cache, arg ...string) string {
if len(arg) > 0 {
bs := md5.Sum([]byte(fmt.Sprintln("用户密码:%s", arg[0])))
m.Assert(x.Value == "" || x.Value == hex.EncodeToString(bs[:]), "密码错误")
m.Cap("expire", fmt.Sprintf("%d", time.Now().Unix()+int64(Pulse.Confi("expire"))))
return hex.EncodeToString(bs[:])
}
return x.Value
}}
aaa.Caches["sessid"] = &ctx.Cache{Name: "会话令牌", Value: "", Help: "用户的会话标识"}
aaa.Caches["expire"] = &ctx.Cache{Name: "会话超时", Value: "", Help: "用户的会话标识"}
aaa.Caches["time"] = &ctx.Cache{Name: "登录时间", Value: fmt.Sprintf("%d", time.Now().Unix()), Help: "用户登录时间", Hand: func(m *ctx.Message, x *ctx.Cache, arg ...string) string {
if len(arg) > 0 {
return arg[0]
}
n, e := strconv.Atoi(x.Value)
m.Assert(e)
return time.Unix(int64(n), 0).Format("15:03:04")
}}
if m.Target() == Index {
Pulse = m
}
return aaa
}
// }}}
func (aaa *AAA) Start(m *ctx.Message, arg ...string) bool { // {{{
if len(arg) > 1 && m.Cap("sessid") == "" {
m.Cap("group", arg[0])
m.Cap("username", arg[1])
m.Cap("stream", m.Cap("username"))
m.Cap("sessid", aaa.Session(arg[1]))
Pulse.Capi("nuser", 1)
aaa.Owner = aaa.Context
}
m.Log("info", m.Source(), "%s login %s %s", Pulse.Cap("nuser"), m.Cap("group"), m.Cap("username"))
return false
}
// }}}
func (aaa *AAA) Close(m *ctx.Message, arg ...string) bool { // {{{
switch aaa.Context {
case m.Target():
root := Pulse.Target().Server.(*AAA)
delete(root.sessions, m.Cap("sessid"))
m.Log("info", nil, "%d logout %s", Pulse.Capi("nuser", -1)+1, m.Cap("username"))
case m.Source():
}
return true
}
// }}}
var Pulse *ctx.Message
var Index = &ctx.Context{Name: "aaa", Help: "认证中心",
Caches: map[string]*ctx.Cache{
"nuser": &ctx.Cache{Name: "用户数量", Value: "0", Help: "用户数量"},
},
Configs: map[string]*ctx.Config{
"rootname": &ctx.Config{Name: "根用户名", Value: "root", Help: "根用户名"},
"expire": &ctx.Config{Name: "会话超时(s)", Value: "7200", Help: "会话超时"},
},
Commands: map[string]*ctx.Command{
"login": &ctx.Command{Name: "login [sessid]|[[group] username password]]", Help: "用户登录", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
m.Target(c) // {{{
aaa := c.Server.(*AAA)
switch len(arg) {
case 0:
m.Travel(c, func(m *ctx.Message) bool {
m.Echo("%s(%s): %s\n", m.Target().Name, m.Cap("group"), m.Cap("time"))
if int64(m.Capi("expire")) < time.Now().Unix() {
m.Target().Close(m)
}
return true
})
case 1:
s, ok := aaa.sessions[arg[0]]
m.Assert(ok, "会话失败")
m.Target(s)
m.Assert(int64(m.Capi("expire")) > time.Now().Unix(), "会话失败")
m.Log("info", m.Source(), "logon %s %s", m.Cap("username"), m.Cap("group"))
m.Echo(m.Cap("username"))
m.Append("username", m.Cap("username"))
m.Append("userrole", m.Cap("group"))
m.Appendv("aaa", m)
m.Sesss("aaa", m)
case 2, 3:
group, username, password := arg[0], arg[0], arg[1]
if len(arg) == 3 {
username, password = arg[1], arg[2]
}
msg := m
if username == Pulse.Conf("rootname") {
msg = Pulse.Spawn(Pulse.Target())
ctx.Index.Sessions["aaa"] = msg
msg.Set("detail", group, username).Target().Start(msg)
} else if msg = Pulse.Find(username, false); msg == nil {
m.Start(username, "认证用户", group, username)
msg = m
} else {
m.Target(msg.Target())
}
msg.Target().Sessions["aaa"] = msg
msg.Cap("password", password)
m.Login(msg)
aaa.sessions[m.Cap("sessid")] = msg.Target()
m.Echo(msg.Cap("sessid"))
m.Append("username", msg.Cap("username"))
m.Append("userrole", msg.Cap("group"))
m.Appendv("aaa", msg)
m.Sesss("aaa", msg)
}
// }}}
}},
"share": &ctx.Command{Name: "share user", Help: "用户登录", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if len(arg) == 0 { // {{{
aaa := m.Target().Server.(*AAA)
for k, v := range aaa.share {
m.Echo("%s: %s", k, v.Name)
}
return
}
group := m.Sess("aaa").Cap("group")
m.Travel(c, func(msg *ctx.Message) bool {
aaa := msg.Target().Server.(*AAA)
if aaa.share == nil {
aaa.share = make(map[string]*ctx.Context)
}
aaa.share[group] = m.Target()
return true
})
// }}}
}},
"md5": &ctx.Command{Name: "md5 [file filename][content]", Help: "散列",
Form: map[string]int{"file": 1},
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if m.Options("file") { // {{{
f, e := os.Open(m.Option("file"))
m.Assert(e)
h := md5.New()
io.Copy(h, f)
m.Echo(hex.EncodeToString(h.Sum([]byte{})[:]))
} else if len(arg) > 0 {
h := md5.Sum([]byte(arg[0]))
m.Echo(hex.EncodeToString(h[:]))
}
// }}}
}},
"rsa": &ctx.Command{Name: "rsa gen|encrypt|decrypt|sign|verify [keyfile filename][key str][mmfile filename][mm str][signfile filename][signs str][file filename] content",
Help: ` gen生成密钥, encrypt公钥加密, decrypt私钥解密, sgin私钥签名, verify公钥验签,
keyfile密钥文件, key密钥字符串mm加密文件, mm加密字符串, signfile签名文件signs签名字符串,
file数据文件content数据内容.
密钥: rsa gen keyfile key.pem
加密: rsa encrypt keyfile pubkey.pem mmfile mm.txt hello
解密: rsa decrypt keyfile key.pem mmfile mm.txt
签名: rsa sign keyfile key.pem signfile sign.txt hello
验签: rsa verify keyfile pubkey.pem signfile sign.txt hello`,
Form: map[string]int{"keyfile": 1, "key": 1, "mmfile": 1, "mm": 1, "signfile": 1, "signs": 1, "file": 1},
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if arg[0] == "gen" { // {{{
keys, e := rsa.GenerateKey(crand.Reader, 1024)
m.Assert(e)
private := string(pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(keys)}))
m.Append("private", private)
m.Echo(private)
pub, e := x509.MarshalPKIXPublicKey(&keys.PublicKey)
m.Assert(e)
public := string(pem.EncodeToMemory(&pem.Block{Type: "RSA PUBLIC KEY", Bytes: pub}))
m.Append("public", public)
m.Echo(public)
if m.Options("keyfile") {
ioutil.WriteFile(m.Option("keyfile"), []byte(private), 0666)
ioutil.WriteFile("pub"+m.Option("keyfile"), []byte(public), 0666)
}
return
}
keys := []byte(m.Option("key"))
if m.Options("keyfile") {
b, e := ioutil.ReadFile(m.Option("keyfile"))
m.Assert(e)
keys = b
}
block, e := pem.Decode(keys)
m.Assert(e)
if arg[0] == "decrypt" {
private, e := x509.ParsePKCS1PrivateKey(block.Bytes)
m.Assert(e)
mm := []byte(m.Option("mm"))
if m.Options("mmfile") {
b, e := ioutil.ReadFile(m.Option("mmfile"))
m.Assert(e)
mm = b
}
buf := make([]byte, 1024)
n, e := base64.StdEncoding.Decode(buf, mm)
m.Assert(e)
buf = buf[:n]
b, e := rsa.DecryptPKCS1v15(crand.Reader, private, buf)
m.Assert(e)
m.Echo(string(b))
if m.Options("file") {
ioutil.WriteFile(m.Option("file"), b, 0666)
}
return
}
var content []byte
if m.Options("file") {
b, e := ioutil.ReadFile(m.Option("file"))
m.Assert(e)
content = b
} else if len(arg) > 1 {
content = []byte(arg[1])
}
switch arg[0] {
case "encrypt":
public, e := x509.ParsePKIXPublicKey(block.Bytes)
m.Assert(e)
b, e := rsa.EncryptPKCS1v15(crand.Reader, public.(*rsa.PublicKey), content)
m.Assert(e)
res := base64.StdEncoding.EncodeToString(b)
m.Echo(res)
if m.Options("mmfile") {
ioutil.WriteFile(m.Option("mmfile"), []byte(res), 0666)
}
case "sign":
private, e := x509.ParsePKCS1PrivateKey(block.Bytes)
m.Assert(e)
h := md5.Sum(content)
b, e := rsa.SignPKCS1v15(crand.Reader, private, crypto.MD5, h[:])
m.Assert(e)
res := base64.StdEncoding.EncodeToString(b)
m.Echo(res)
if m.Options("signfile") {
ioutil.WriteFile(m.Option("signfile"), []byte(res), 0666)
}
case "verify":
public, e := x509.ParsePKIXPublicKey(block.Bytes)
m.Assert(e)
sign := []byte(m.Option("sign"))
if m.Options("signfile") {
b, e := ioutil.ReadFile(m.Option("signfile"))
m.Assert(e)
sign = b
}
buf := make([]byte, 1024)
n, e := base64.StdEncoding.Decode(buf, sign)
m.Assert(e)
buf = buf[:n]
h := md5.Sum(content)
m.Echo("%t", rsa.VerifyPKCS1v15(public.(*rsa.PublicKey), crypto.MD5, h[:], buf) == nil)
}
// }}}
}},
"deal": &ctx.Command{Name: "deal init|sell|buy|done [keyfile name][key str]", Help: "散列",
Form: map[string]int{"file": 1},
Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) {
if m.Options("file") { // {{{
f, e := os.Open(m.Option("file"))
m.Assert(e)
h := md5.New()
io.Copy(h, f)
m.Echo(hex.EncodeToString(h.Sum([]byte{})[:]))
} else if len(arg) > 0 {
h := md5.Sum([]byte(arg[0]))
m.Echo(hex.EncodeToString(h[:]))
}
// }}}
}},
},
Index: map[string]*ctx.Context{
"void": &ctx.Context{Name: "void", Help: "void",
Caches: map[string]*ctx.Cache{"group": &ctx.Cache{}},
Configs: map[string]*ctx.Config{"rootname": &ctx.Config{}},
Commands: map[string]*ctx.Command{
"login": &ctx.Command{},
"check": &ctx.Command{},
},
},
},
}
func init() {
aaa := &AAA{}
aaa.Context = Index
ctx.Index.Register(Index, aaa)
aaa.sessions = make(map[string]*ctx.Context)
}