mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 02:37:24 +08:00
Merge pull request #1411 from swordqiu/hotfix/qj-misc-bugfix-20190626
fix: 1. auto create project after import domains 2. add actionlogs for dentity resources
This commit is contained in:
@@ -116,11 +116,27 @@ func init() {
|
||||
|
||||
type IdentityProviderCreateOptions struct {
|
||||
NAME string `help:"name of identity provider" json:"-"`
|
||||
|
||||
AutoCreateProject bool `help:"automatically create a default project when importing domain" json:"-"`
|
||||
NoAutoCreateProject bool `help:"do not create default project when importing domain" json:"-"`
|
||||
|
||||
TargetDomain string `help:"target domain without creating new domain"`
|
||||
|
||||
api.SLDAPIdpConfigOptions
|
||||
}
|
||||
R(&IdentityProviderCreateOptions{}, "idp-create-ldap", "Create an identity provider with LDAP driver", func(s *mcclient.ClientSession, args *IdentityProviderCreateOptions) error {
|
||||
params := jsonutils.NewDict()
|
||||
params.Add(jsonutils.NewString(args.NAME), "name")
|
||||
|
||||
if len(args.TargetDomain) > 0 {
|
||||
params.Add(jsonutils.NewString(args.TargetDomain), "target_domain")
|
||||
}
|
||||
if args.AutoCreateProject {
|
||||
params.Add(jsonutils.JSONTrue, "auto_create_project")
|
||||
} else if args.NoAutoCreateProject {
|
||||
params.Add(jsonutils.JSONFalse, "auto_create_project")
|
||||
}
|
||||
|
||||
params.Add(jsonutils.NewString("ldap"), "driver")
|
||||
params.Add(jsonutils.Marshal(args), "config", "ldap")
|
||||
|
||||
@@ -150,6 +166,12 @@ func init() {
|
||||
type IdentityProviderCreateLDAPSingleDomainOptions struct {
|
||||
NAME string `help:"name of identity provider" json:"-"`
|
||||
TEMPLATE string `help:"configuration template name" choices:"msad_one_domain|openldap_one_domain" json:"-"`
|
||||
|
||||
AutoCreateProject bool `help:"automatically create a default project when importing domain" json:"-"`
|
||||
NoAutoCreateProject bool `help:"do not create default project when importing domain" json:"-"`
|
||||
|
||||
TargetDomain string `help:"target domain without creating new domain"`
|
||||
|
||||
api.SLDAPIdpConfigSingleDomainOptions
|
||||
}
|
||||
R(&IdentityProviderCreateLDAPSingleDomainOptions{}, "idp-create-ldap-single-domain", "Create an identity provider with LDAP driver/single domain template", func(s *mcclient.ClientSession, args *IdentityProviderCreateLDAPSingleDomainOptions) error {
|
||||
@@ -157,6 +179,16 @@ func init() {
|
||||
params.Add(jsonutils.NewString(args.NAME), "name")
|
||||
params.Add(jsonutils.NewString("ldap"), "driver")
|
||||
params.Add(jsonutils.NewString(args.TEMPLATE), "template")
|
||||
|
||||
if len(args.TargetDomain) > 0 {
|
||||
params.Add(jsonutils.NewString(args.TargetDomain), "target_domain")
|
||||
}
|
||||
if args.AutoCreateProject {
|
||||
params.Add(jsonutils.JSONTrue, "auto_create_project")
|
||||
} else if args.NoAutoCreateProject {
|
||||
params.Add(jsonutils.JSONFalse, "auto_create_project")
|
||||
}
|
||||
|
||||
params.Add(jsonutils.Marshal(args), "config", "ldap")
|
||||
|
||||
idp, err := modules.IdentityProviders.Create(s, params)
|
||||
@@ -185,6 +217,10 @@ func init() {
|
||||
type IdentityProviderCreateLDAPMultiDomainOptions struct {
|
||||
NAME string `help:"name of identity provider" json:"-"`
|
||||
TEMPLATE string `help:"configuration template name" choices:"msad_multi_domain" json:"-"`
|
||||
|
||||
AutoCreateProject bool `help:"automatically create a default project when importing domain" json:"-"`
|
||||
NoAutoCreateProject bool `help:"do not create default project when importing domain" json:"-"`
|
||||
|
||||
api.SLDAPIdpConfigMultiDomainOptions
|
||||
}
|
||||
R(&IdentityProviderCreateLDAPMultiDomainOptions{}, "idp-create-ldap-multi-domain", "Create an identity provider with LDAP driver/single domain template", func(s *mcclient.ClientSession, args *IdentityProviderCreateLDAPMultiDomainOptions) error {
|
||||
@@ -192,6 +228,13 @@ func init() {
|
||||
params.Add(jsonutils.NewString(args.NAME), "name")
|
||||
params.Add(jsonutils.NewString("ldap"), "driver")
|
||||
params.Add(jsonutils.NewString(args.TEMPLATE), "template")
|
||||
|
||||
if args.AutoCreateProject {
|
||||
params.Add(jsonutils.JSONTrue, "auto_create_project")
|
||||
} else if args.NoAutoCreateProject {
|
||||
params.Add(jsonutils.JSONFalse, "auto_create_project")
|
||||
}
|
||||
|
||||
params.Add(jsonutils.Marshal(args), "config", "ldap")
|
||||
|
||||
idp, err := modules.IdentityProviders.Create(s, params)
|
||||
|
||||
@@ -86,21 +86,19 @@ require (
|
||||
github.com/kr/pty v1.1.3
|
||||
github.com/libvirt/libvirt-go-xml v5.2.0+incompatible
|
||||
github.com/marstr/guid v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.10.0 // indirect
|
||||
github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f // indirect
|
||||
github.com/mdlayher/arp v0.0.0-20190313224443-98a83c8a2717
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7
|
||||
github.com/mdlayher/raw v0.0.0-20190606144222-a54781e5f38f
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mholt/caddy v0.10.11
|
||||
github.com/miekg/dns v1.1.1
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/moul/http2curl v1.0.0
|
||||
github.com/mozillazg/go-pinyin v0.15.0
|
||||
github.com/nelsonken/cos-go-sdk-v5 v0.0.0-20180622024522-5247afdb7a80
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
|
||||
github.com/opentracing/opentracing-go v1.0.2 // indirect
|
||||
@@ -116,13 +114,11 @@ require (
|
||||
github.com/serialx/hashring v0.0.0-20180504054112-49a4782e9908
|
||||
github.com/shirou/gopsutil v2.18.10+incompatible
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
|
||||
github.com/sirupsen/logrus v1.2.0 // indirect
|
||||
github.com/soheilhy/cmux v0.1.4 // indirect
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 // indirect
|
||||
github.com/spf13/pflag v1.0.3 // indirect
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v0.0.0-20181108132626-805d01dd0e2e
|
||||
github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e // indirect
|
||||
github.com/tinylib/msgp v1.1.0 // indirect
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
|
||||
github.com/tredoe/osutil v0.0.0-20161130133508-7d3ee1afa71c
|
||||
@@ -135,7 +131,7 @@ require (
|
||||
go.uber.org/atomic v1.4.0 // indirect
|
||||
go.uber.org/multierr v1.1.0 // indirect
|
||||
go.uber.org/zap v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 // indirect
|
||||
golang.org/x/sys v0.0.0-20190620070143-6f217b454f45
|
||||
@@ -154,9 +150,9 @@ require (
|
||||
k8s.io/klog v0.1.0 // indirect
|
||||
k8s.io/kubernetes v1.12.3
|
||||
sigs.k8s.io/yaml v1.1.0 // indirect
|
||||
yunion.io/x/jsonutils v0.0.0-20190613101330-71384b7a9233
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051
|
||||
yunion.io/x/log v0.0.0-20190514041436-04ce53b17c6b
|
||||
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf
|
||||
yunion.io/x/sqlchemy v0.0.0-20190624021043-a2ed237bb1f7
|
||||
yunion.io/x/structarg v0.0.0-20190622051356-20d0c9b7b3cd
|
||||
yunion.io/x/structarg v0.0.0-20190625074850-3c0636a9fffe
|
||||
)
|
||||
|
||||
@@ -243,11 +243,11 @@ github.com/libvirt/libvirt-go-xml v5.2.0+incompatible h1:ALyVpa0/lFfFaUpb5l0fNBo
|
||||
github.com/libvirt/libvirt-go-xml v5.2.0+incompatible/go.mod h1:oBlgD3xOA01ihiK5stbhFzvieyW+jVS6kbbsMVF623A=
|
||||
github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI=
|
||||
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-sqlite3 v1.7.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
@@ -280,6 +280,8 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||
github.com/mozillazg/go-pinyin v0.15.0 h1:sSwlnsogK/WMzcf0HnjgxyAI4GU6LFqwXnhr77q1Z80=
|
||||
github.com/mozillazg/go-pinyin v0.15.0/go.mod h1:bO+dztNW6O2lSJdYLha7LO3bujXzjjU3UvKb2IGANfg=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/nelsonken/cos-go-sdk-v5 v0.0.0-20180622024522-5247afdb7a80 h1:1tGm26e9ktdEIa7LHD7xf7oMUCE0V2wP4URkUAvEttA=
|
||||
@@ -328,8 +330,8 @@ github.com/shirou/gopsutil v2.18.10+incompatible h1:cy84jW6EVRPa5g9HAHrlbxMSIjBh
|
||||
github.com/shirou/gopsutil v2.18.10+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180607162144-eb5b59917fa2 h1:hjkfjJKpNSPqJZJKSrHbQgBz+eEui8ivYlorRc9DR64=
|
||||
github.com/smartystreets/assertions v0.0.0-20180607162144-eb5b59917fa2/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo=
|
||||
@@ -385,9 +387,10 @@ go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -409,12 +412,14 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTm
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180724212812-e072cadbbdc8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190620070143-6f217b454f45 h1:Dl2hc890lrizvUppGbRWhnIh2f8jOTCQpY5IKWRS0oM=
|
||||
golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -466,13 +471,13 @@ k8s.io/kubernetes v1.12.3 h1:5GPfYyyBylqcZUqL+ApYpYTm2IYjH56JUewYC0GbetU=
|
||||
k8s.io/kubernetes v1.12.3/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
yunion.io/x/jsonutils v0.0.0-20190613101330-71384b7a9233 h1:5UK32emYQKh+yudRak1/SB/MGMPVGL7IYuw0TaNqy0A=
|
||||
yunion.io/x/jsonutils v0.0.0-20190613101330-71384b7a9233/go.mod h1:phWuXiaaZXZbftdpKIXCNgn+0pVurDOvV/Iv2sK6HKE=
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051 h1:vtZw2iwGrsARNSwRTREGjmr2BWPdxbmXVkb3kI1qu28=
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
|
||||
yunion.io/x/log v0.0.0-20190514041436-04ce53b17c6b h1:Z9z+7iegu0HXuL+S8taVWRd1P4b9JJOgPXIeoqYrj7c=
|
||||
yunion.io/x/log v0.0.0-20190514041436-04ce53b17c6b/go.mod h1:+gauLs73omeJAPlsXcevLsJLKixV+sR/E7WSYTSx1fE=
|
||||
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf h1:OsKC+2ghZHwp+Ztm/MwKlLKKRiE7QcPG8eTp0GmsHbg=
|
||||
yunion.io/x/pkg v0.0.0-20190620104149-945c25821dbf/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
|
||||
yunion.io/x/sqlchemy v0.0.0-20190624021043-a2ed237bb1f7 h1:mGfap/bAn7ghA6w6cBxzTiTzSF/pR3C2AEXlOCHc12A=
|
||||
yunion.io/x/sqlchemy v0.0.0-20190624021043-a2ed237bb1f7/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI=
|
||||
yunion.io/x/structarg v0.0.0-20190622051356-20d0c9b7b3cd h1:uBl6EWUmCujJpkDEYO/xnTfOjjphVVaF7arFp9ELmi0=
|
||||
yunion.io/x/structarg v0.0.0-20190622051356-20d0c9b7b3cd/go.mod h1:cMBQJeg4CLJF4yDTWz7UPb5b1u97k9d9gcsefsUoYvI=
|
||||
yunion.io/x/structarg v0.0.0-20190625074850-3c0636a9fffe h1:Wr2EXv72ynwtW+x0VCqWwpsfUBWecaZWnLMgaP3UiTo=
|
||||
yunion.io/x/structarg v0.0.0-20190625074850-3c0636a9fffe/go.mod h1:EP6NSv2C0zzqBDTKumv8hPWLb3XvgMZDHQRfyuOrQng=
|
||||
|
||||
@@ -47,12 +47,10 @@ type SLDAPIdpConfigOptions struct {
|
||||
Url string `json:"url,omitempty" help:"LDAP server URL" required:"true"`
|
||||
Suffix string `json:"suffix,omitempty" required:"true"`
|
||||
QueryScope string `json:"query_scope,omitempty" help:"Query scope, either one or sub" choices:"one|sub"`
|
||||
// PageSize int `json:"page_size,omitzero" help:"Page size, default 20" default:"20"`
|
||||
|
||||
User string `json:"user,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
|
||||
ImportDomain bool `json:"import_domain,omitfalse"`
|
||||
|
||||
DomainTreeDN string `json:"domain_tree_dn,omitempty" help:"Domain tree root node dn(distinguished name)"`
|
||||
DomainFilter string `json:"domain_filter,omitempty"`
|
||||
DomainObjectclass string `json:"domain_objectclass,omitempty"`
|
||||
|
||||
@@ -59,6 +59,7 @@ const (
|
||||
IdentityDriverStatusConnected = "connected"
|
||||
IdentityDriverStatusDisconnected = "disconnected"
|
||||
IdentityDriverStatusDeleting = "deleting"
|
||||
IdentityDriverStatusDeleteFailed = "delete_fail"
|
||||
|
||||
IdentityProviderSyncLocal = "local"
|
||||
IdentityProviderSyncFull = "full"
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
func FetchJointByIds(manager IJointModelManager, masterId, slaveId string, query jsonutils.JSONObject) (IJointModel, error) {
|
||||
@@ -127,6 +128,9 @@ func FetchByName(manager IModelManager, userCred mcclient.IIdentityProvider, idS
|
||||
}
|
||||
|
||||
func FetchByIdOrName(manager IModelManager, userCred mcclient.IIdentityProvider, idStr string) (IModel, error) {
|
||||
if stringutils2.IsUtf8(idStr) {
|
||||
return FetchByName(manager, userCred, idStr)
|
||||
}
|
||||
obj, err := FetchById(manager, idStr)
|
||||
if err == sql.ErrNoRows {
|
||||
return FetchByName(manager, userCred, idStr)
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
type STenantCacheManager struct {
|
||||
@@ -123,10 +124,14 @@ func (manager *STenantCacheManager) fetchTenant(ctx context.Context, idStr strin
|
||||
|
||||
func (manager *STenantCacheManager) FetchTenantByIdOrName(ctx context.Context, idStr string) (*STenant, error) {
|
||||
return manager.fetchTenant(ctx, idStr, false, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
return q.Filter(sqlchemy.OR(
|
||||
sqlchemy.Equals(q.Field("id"), idStr),
|
||||
sqlchemy.Equals(q.Field("name"), idStr),
|
||||
))
|
||||
if stringutils2.IsUtf8(idStr) {
|
||||
return q.Equals("name", idStr)
|
||||
} else {
|
||||
return q.Filter(sqlchemy.OR(
|
||||
sqlchemy.Equals(q.Field("id"), idStr),
|
||||
sqlchemy.Equals(q.Field("name"), idStr),
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -167,10 +172,14 @@ func (manager *STenantCacheManager) fetchTenantFromKeystone(ctx context.Context,
|
||||
|
||||
func (manager *STenantCacheManager) FetchDomainByIdOrName(ctx context.Context, idStr string) (*STenant, error) {
|
||||
return manager.fetchTenant(ctx, idStr, true, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
return q.Filter(sqlchemy.OR(
|
||||
sqlchemy.Equals(q.Field("id"), idStr),
|
||||
sqlchemy.Equals(q.Field("name"), idStr),
|
||||
))
|
||||
if stringutils2.IsUtf8(idStr) {
|
||||
return q.Equals("name", idStr)
|
||||
} else {
|
||||
return q.Filter(sqlchemy.OR(
|
||||
sqlchemy.Equals(q.Field("id"), idStr),
|
||||
sqlchemy.Equals(q.Field("name"), idStr),
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -925,7 +925,8 @@ func syncPublicCloudProviderInfo(
|
||||
|
||||
log.Debugf("storageCachePairs count %d", len(storageCachePairs))
|
||||
for i := range storageCachePairs {
|
||||
if storageCachePairs[i].isNew || syncRange.DeepSync {
|
||||
// always sync private cloud cached images
|
||||
if storageCachePairs[i].isNew || syncRange.DeepSync || !driver.GetFactory().IsPublicCloud() {
|
||||
result := storageCachePairs[i].syncCloudImages(ctx, userCred)
|
||||
|
||||
syncResults.Add(StoragecachedimageManager, result)
|
||||
@@ -991,12 +992,13 @@ func syncOnPremiseCloudProviderInfo(
|
||||
|
||||
log.Debugf("storageCachePairs count %d", len(storageCachePairs))
|
||||
for i := range storageCachePairs {
|
||||
if storageCachePairs[i].isNew || syncRange.DeepSync {
|
||||
result := storageCachePairs[i].syncCloudImages(ctx, userCred)
|
||||
syncResults.Add(StoragecachedimageManager, result)
|
||||
msg := result.Result()
|
||||
log.Infof("syncCloudImages result: %s", msg)
|
||||
}
|
||||
// alway sync on-premise cached images
|
||||
// if storageCachePairs[i].isNew || syncRange.DeepSync {
|
||||
result := storageCachePairs[i].syncCloudImages(ctx, userCred)
|
||||
syncResults.Add(StoragecachedimageManager, result)
|
||||
msg := result.Result()
|
||||
log.Infof("syncCloudImages result: %s", msg)
|
||||
// }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ func GetDriverClass(drv string) IIdentityBackendClass {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDriver(driver string, idpId, idpName, template string, conf api.TIdentityProviderConfigs) (IIdentityBackend, error) {
|
||||
func GetDriver(driver string, idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (IIdentityBackend, error) {
|
||||
drvCls := GetDriverClass(driver)
|
||||
if drvCls == nil {
|
||||
return nil, ErrNoSuchDriver
|
||||
}
|
||||
return drvCls.NewDriver(idpId, idpName, template, conf)
|
||||
return drvCls.NewDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf)
|
||||
}
|
||||
|
||||
type SBaseIdentityDriver struct {
|
||||
@@ -49,17 +49,22 @@ type SBaseIdentityDriver struct {
|
||||
IdpId string
|
||||
IdpName string
|
||||
Template string
|
||||
|
||||
TargetDomainId string
|
||||
AutoCreateProject bool
|
||||
}
|
||||
|
||||
func (base *SBaseIdentityDriver) IIdentityBackend() IIdentityBackend {
|
||||
return base.GetVirtualObject().(IIdentityBackend)
|
||||
}
|
||||
|
||||
func NewBaseIdentityDriver(idpId, idpName, template string, conf api.TIdentityProviderConfigs) (SBaseIdentityDriver, error) {
|
||||
func NewBaseIdentityDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (SBaseIdentityDriver, error) {
|
||||
drv := SBaseIdentityDriver{}
|
||||
drv.IdpId = idpId
|
||||
drv.IdpName = idpName
|
||||
drv.Template = template
|
||||
drv.TargetDomainId = targetDomainId
|
||||
drv.AutoCreateProject = autoCreateProject
|
||||
drv.Config = conf
|
||||
return drv, nil
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ type IIdentityBackendClass interface {
|
||||
SingletonInstance() bool
|
||||
SyncMethod() string
|
||||
Name() string
|
||||
NewDriver(idpId, idpName, template string, conf api.TIdentityProviderConfigs) (IIdentityBackend, error)
|
||||
NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (IIdentityBackend, error)
|
||||
}
|
||||
|
||||
type IIdentityBackend interface {
|
||||
|
||||
@@ -29,8 +29,8 @@ func (self *SLDAPDriverClass) SyncMethod() string {
|
||||
return api.IdentityProviderSyncFull
|
||||
}
|
||||
|
||||
func (self *SLDAPDriverClass) NewDriver(idpId, idpName, template string, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) {
|
||||
return NewLDAPDriver(idpId, idpName, template, conf)
|
||||
func (self *SLDAPDriverClass) NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) {
|
||||
return NewLDAPDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf)
|
||||
}
|
||||
|
||||
func (self *SLDAPDriverClass) Name() string {
|
||||
|
||||
@@ -21,10 +21,9 @@ import (
|
||||
|
||||
"gopkg.in/ldap.v3"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
@@ -40,8 +39,8 @@ type SLDAPDriver struct {
|
||||
ldapConfig *api.SLDAPIdpConfigOptions
|
||||
}
|
||||
|
||||
func NewLDAPDriver(idpId, idpName, template string, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) {
|
||||
base, err := driver.NewBaseIdentityDriver(idpId, idpName, template, conf)
|
||||
func NewLDAPDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) {
|
||||
base, err := driver.NewBaseIdentityDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NewBaseIdentityDriver")
|
||||
}
|
||||
@@ -144,26 +143,26 @@ func (self *SLDAPDriver) domainAttributeList() []string {
|
||||
func (self *SLDAPDriver) entry2Domain(entry *ldap.Entry) SDomainInfo {
|
||||
info := SDomainInfo{}
|
||||
info.DN = entry.DN
|
||||
info.Id = entry.GetAttributeValue(self.ldapConfig.DomainIdAttribute)
|
||||
info.Name = entry.GetAttributeValue(self.ldapConfig.DomainNameAttribute)
|
||||
info.Id = ldaputils.GetAttributeValue(entry, self.ldapConfig.DomainIdAttribute)
|
||||
info.Name = ldaputils.GetAttributeValue(entry, self.ldapConfig.DomainNameAttribute)
|
||||
return info
|
||||
}
|
||||
|
||||
func (self *SLDAPDriver) entry2Group(entry *ldap.Entry) SGroupInfo {
|
||||
info := SGroupInfo{}
|
||||
info.DN = entry.DN
|
||||
info.Id = entry.GetAttributeValue(self.ldapConfig.GroupIdAttribute)
|
||||
info.Name = entry.GetAttributeValue(self.ldapConfig.GroupNameAttribute)
|
||||
info.Members = entry.GetAttributeValues(self.ldapConfig.GroupMemberAttribute)
|
||||
info.Id = ldaputils.GetAttributeValue(entry, self.ldapConfig.GroupIdAttribute)
|
||||
info.Name = ldaputils.GetAttributeValue(entry, self.ldapConfig.GroupNameAttribute)
|
||||
info.Members = ldaputils.GetAttributeValues(entry, self.ldapConfig.GroupMemberAttribute)
|
||||
return info
|
||||
}
|
||||
|
||||
func (self *SLDAPDriver) entry2User(entry *ldap.Entry) SUserInfo {
|
||||
info := SUserInfo{}
|
||||
info.DN = entry.DN
|
||||
info.Id = entry.GetAttributeValue(self.ldapConfig.UserIdAttribute)
|
||||
info.Name = entry.GetAttributeValue(self.ldapConfig.UserNameAttribute)
|
||||
enabledStr := entry.GetAttributeValue(self.ldapConfig.UserEnabledAttribute)
|
||||
info.Id = ldaputils.GetAttributeValue(entry, self.ldapConfig.UserIdAttribute)
|
||||
info.Name = ldaputils.GetAttributeValue(entry, self.ldapConfig.UserNameAttribute)
|
||||
enabledStr := ldaputils.GetAttributeValue(entry, self.ldapConfig.UserEnabledAttribute)
|
||||
if len(enabledStr) == 0 {
|
||||
enabledStr = self.ldapConfig.UserEnabledDefault
|
||||
}
|
||||
@@ -182,7 +181,7 @@ func (self *SLDAPDriver) entry2User(entry *ldap.Entry) SUserInfo {
|
||||
for _, m := range self.ldapConfig.UserAdditionalAttribute {
|
||||
parts := strings.Split(m, ":")
|
||||
if len(parts) == 2 {
|
||||
info.Extra[parts[1]] = entry.GetAttributeValue(parts[0])
|
||||
info.Extra[parts[1]] = ldaputils.GetAttributeValue(entry, parts[0])
|
||||
}
|
||||
}
|
||||
return info
|
||||
@@ -242,12 +241,20 @@ func (self *SLDAPDriver) Authenticate(ctx context.Context, ident mcclient.SAuthe
|
||||
}
|
||||
|
||||
var userTreeDN string
|
||||
if self.ldapConfig.ImportDomain {
|
||||
if len(self.ldapConfig.DomainTreeDN) > 0 {
|
||||
// import domains
|
||||
idMap, err := models.IdmappingManager.FetchEntity(usrExt.DomainId, api.IdMappingEntityDomain)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "IdmappingManager.FetchEntity for domain")
|
||||
}
|
||||
userTreeDN = idMap.IdpEntityId
|
||||
entries, err := self.searchDomainEntries(cli, idMap.IdpEntityId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "self.searchDomainEntries")
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
return nil, errors.Error("fail to find domain DN")
|
||||
}
|
||||
userTreeDN = entries[0].DN
|
||||
} else {
|
||||
userTreeDN = self.getUserTreeDN()
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/ldap.v3"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
@@ -46,7 +48,7 @@ func (self *SLDAPDriver) Sync(ctx context.Context) error {
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
if self.ldapConfig.ImportDomain {
|
||||
if len(self.ldapConfig.DomainTreeDN) > 0 {
|
||||
return self.syncDomains(ctx, cli)
|
||||
} else {
|
||||
return self.syncSingleDomain(ctx, cli)
|
||||
@@ -54,10 +56,25 @@ func (self *SLDAPDriver) Sync(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (self *SLDAPDriver) syncSingleDomain(ctx context.Context, cli *ldaputils.SLDAPClient) error {
|
||||
domainInfo := SDomainInfo{DN: self.ldapConfig.Suffix, Id: api.DefaultRemoteDomainId, Name: self.IdpName}
|
||||
domain, err := self.syncDomainInfo(ctx, domainInfo)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "syncDomainInfo")
|
||||
var domain *models.SDomain
|
||||
if len(self.TargetDomainId) > 0 {
|
||||
targetDomain, err := models.DomainManager.FetchDomainById(self.TargetDomainId)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "models.DomainManager.FetchDomainById")
|
||||
}
|
||||
if targetDomain == nil {
|
||||
log.Warningln("target domain not exist!")
|
||||
} else {
|
||||
domain = targetDomain
|
||||
}
|
||||
}
|
||||
if domain == nil {
|
||||
domainInfo := SDomainInfo{DN: self.ldapConfig.Suffix, Id: api.DefaultRemoteDomainId, Name: self.IdpName}
|
||||
newDomain, err := self.syncDomainInfo(ctx, domainInfo)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "syncDomainInfo")
|
||||
}
|
||||
domain = newDomain
|
||||
}
|
||||
userIdMap, err := self.syncUsers(ctx, cli, domain.Id, self.getUserTreeDN())
|
||||
if err != nil {
|
||||
@@ -70,14 +87,22 @@ func (self *SLDAPDriver) syncSingleDomain(ctx context.Context, cli *ldaputils.SL
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SLDAPDriver) syncDomains(ctx context.Context, cli *ldaputils.SLDAPClient) error {
|
||||
entries, err := cli.Search(self.getDomainTreeDN(),
|
||||
func (self *SLDAPDriver) searchDomainEntries(cli *ldaputils.SLDAPClient, domainid string) ([]*ldap.Entry, error) {
|
||||
attrMap := make(map[string]string)
|
||||
if len(domainid) > 0 {
|
||||
attrMap[self.ldapConfig.DomainIdAttribute] = domainid
|
||||
}
|
||||
return cli.Search(self.getDomainTreeDN(),
|
||||
self.ldapConfig.DomainObjectclass,
|
||||
nil,
|
||||
attrMap,
|
||||
self.ldapConfig.DomainFilter,
|
||||
self.domainAttributeList(),
|
||||
self.domainQueryScope(),
|
||||
)
|
||||
}
|
||||
|
||||
func (self *SLDAPDriver) syncDomains(ctx context.Context, cli *ldaputils.SLDAPClient) error {
|
||||
entries, err := self.searchDomainEntries(cli, "")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "searchLDAP")
|
||||
}
|
||||
@@ -99,7 +124,7 @@ func (self *SLDAPDriver) syncDomains(ctx context.Context, cli *ldaputils.SLDAPCl
|
||||
}
|
||||
}
|
||||
// remove any obsolete domain Id_mappings
|
||||
err = models.IdmappingManager.DeleteAny(self.IdpId, api.IdMappingEntityDomain, domainLocalIds)
|
||||
err = models.IdmappingManager.DeleteAny(self.IdpId, api.IdMappingEntityDomain, domainLocalIds, nil)
|
||||
if err != nil {
|
||||
log.Errorf("delete remvoed remote domain fail %s", err)
|
||||
}
|
||||
@@ -155,6 +180,27 @@ func (self *SLDAPDriver) syncDomainInfo(ctx context.Context, info SDomainInfo) (
|
||||
return nil, errors.Wrap(err, "insert")
|
||||
}
|
||||
|
||||
if self.AutoCreateProject {
|
||||
project := &models.SProject{}
|
||||
project.SetModelManager(models.ProjectManager, project)
|
||||
projectName := models.NormalizeProjectName(fmt.Sprintf("%s_default_project", info.Name))
|
||||
newName, err := db.GenerateName(models.ProjectManager, nil, projectName)
|
||||
if err != nil {
|
||||
// ignore the error
|
||||
log.Errorf("db.GenerateName error %s for default domain project %s", err, projectName)
|
||||
newName = projectName
|
||||
}
|
||||
project.Name = newName
|
||||
project.DomainId = domain.Id
|
||||
project.Description = fmt.Sprintf("Default project for domain %s", info.Name)
|
||||
project.IsDomain = tristate.False
|
||||
project.ParentId = domain.Id
|
||||
err = models.ProjectManager.TableSpec().Insert(project)
|
||||
if err != nil {
|
||||
log.Errorf("models.ProjectManager.Insert fail %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
@@ -184,9 +230,15 @@ func (self *SLDAPDriver) syncUsers(ctx context.Context, cli *ldaputils.SLDAPClie
|
||||
userIdMap[userInfo.DN] = userId
|
||||
}
|
||||
}
|
||||
err = models.IdmappingManager.DeleteAny(self.IdpId, api.IdMappingEntityUser, userLocalIds)
|
||||
deleteUsrIds, err := models.UserManager.FetchUserLocalIdsInDomain(domainId, userLocalIds)
|
||||
if err != nil {
|
||||
log.Errorf("delete removed remote user fail %s", err)
|
||||
return nil, errors.Wrap(err, "models.UserManager.FetchUserIdsInDomain")
|
||||
}
|
||||
if len(deleteUsrIds) > 0 {
|
||||
err = models.IdmappingManager.DeleteAny(self.IdpId, api.IdMappingEntityUser, nil, deleteUsrIds)
|
||||
if err != nil {
|
||||
log.Errorf("delete removed remote user fail %s", err)
|
||||
}
|
||||
}
|
||||
return userIdMap, nil
|
||||
}
|
||||
@@ -251,6 +303,8 @@ func (self *SLDAPDriver) syncUserDB(ctx context.Context, ui SUserInfo, domainId
|
||||
return "", errors.Wrap(err, "models.IdmappingManager.RegisterIdMap")
|
||||
}
|
||||
|
||||
log.Debugf("syncUserDB: %s", userId)
|
||||
|
||||
// insert nonlocal user
|
||||
err = registerNonlocalUser(ctx, ui, userId, domainId)
|
||||
if err != nil {
|
||||
@@ -280,9 +334,15 @@ func (self *SLDAPDriver) syncGroups(ctx context.Context, cli *ldaputils.SLDAPCli
|
||||
return errors.Wrap(err, "syncGroupDB")
|
||||
}
|
||||
}
|
||||
err = models.IdmappingManager.DeleteAny(self.IdpId, api.IdMappingEntityGroup, groupLocalIds)
|
||||
deleteGroupIds, err := models.GroupManager.FetchGroupLocalIdsInDomain(domainId, groupLocalIds)
|
||||
if err != nil {
|
||||
log.Errorf("delete removed remote group fail %s", err)
|
||||
return errors.Wrap(err, "models.GroupManager.FetchGroupIdsInDomain")
|
||||
}
|
||||
if len(groupLocalIds) > 0 {
|
||||
err = models.IdmappingManager.DeleteAny(self.IdpId, api.IdMappingEntityGroup, nil, deleteGroupIds)
|
||||
if err != nil {
|
||||
log.Errorf("delete removed remote group fail %s", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
|
||||
var (
|
||||
MicrosoftActiveDirectorySingleDomainTemplate = api.SLDAPIdpConfigOptions{
|
||||
ImportDomain: false,
|
||||
UserObjectclass: "organizationalPerson",
|
||||
UserIdAttribute: "sAMAccountName",
|
||||
UserNameAttribute: "sAMAccountName",
|
||||
@@ -43,9 +42,8 @@ var (
|
||||
}
|
||||
|
||||
MicrosoftActiveDirectoryMultipleDomainTemplate = api.SLDAPIdpConfigOptions{
|
||||
ImportDomain: true,
|
||||
DomainObjectclass: "organizationalUnit",
|
||||
DomainIdAttribute: "distinguishedName",
|
||||
DomainIdAttribute: "objectGUID",
|
||||
DomainNameAttribute: "name",
|
||||
DomainQueryScope: "one",
|
||||
UserObjectclass: "organizationalPerson",
|
||||
@@ -70,7 +68,6 @@ var (
|
||||
}
|
||||
|
||||
OpenLdapSingleDomainTemplate = api.SLDAPIdpConfigOptions{
|
||||
ImportDomain: false,
|
||||
UserObjectclass: "person",
|
||||
UserIdAttribute: "uid",
|
||||
UserNameAttribute: "uid",
|
||||
|
||||
@@ -29,8 +29,8 @@ func (self *SSQLDriverClass) SyncMethod() string {
|
||||
return api.IdentityProviderSyncLocal
|
||||
}
|
||||
|
||||
func (self *SSQLDriverClass) NewDriver(idpId, idpName, template string, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) {
|
||||
return NewSQLDriver(idpId, idpName, template, conf)
|
||||
func (self *SSQLDriverClass) NewDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) {
|
||||
return NewSQLDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf)
|
||||
}
|
||||
|
||||
func (self *SSQLDriverClass) Name() string {
|
||||
|
||||
@@ -29,8 +29,8 @@ type SSQLDriver struct {
|
||||
driver.SBaseIdentityDriver
|
||||
}
|
||||
|
||||
func NewSQLDriver(idpId, idpName, template string, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) {
|
||||
base, err := driver.NewBaseIdentityDriver(idpId, idpName, template, conf)
|
||||
func NewSQLDriver(idpId, idpName, template, targetDomainId string, autoCreateProject bool, conf api.TIdentityProviderConfigs) (driver.IIdentityBackend, error) {
|
||||
base, err := driver.NewBaseIdentityDriver(idpId, idpName, template, targetDomainId, autoCreateProject, conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ package models
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/log"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/keystone/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
@@ -25,9 +27,25 @@ import (
|
||||
var (
|
||||
defaultAdminCred mcclient.TokenCredential
|
||||
|
||||
defaultClient = mcclient.NewClient("", 300, options.Options.DebugClient, true, "", "")
|
||||
defaultClient *mcclient.Client = nil
|
||||
)
|
||||
|
||||
func getDefaultClient() *mcclient.Client {
|
||||
if defaultClient == nil {
|
||||
defaultClient = mcclient.NewClient("", 300, options.Options.DebugClient, true, "", "")
|
||||
refreshDefaultClientServiceCatalog()
|
||||
}
|
||||
return defaultClient
|
||||
}
|
||||
|
||||
func refreshDefaultClientServiceCatalog() {
|
||||
cata, err := EndpointManager.FetchAll()
|
||||
if err != nil {
|
||||
log.Fatalf("fail to fetch endpoints")
|
||||
}
|
||||
defaultClient.SetServiceCatalog(cata.GetKeystoneCatalogV3())
|
||||
}
|
||||
|
||||
func GetDefaultAdminCred() mcclient.TokenCredential {
|
||||
if defaultAdminCred == nil {
|
||||
defaultAdminCred = getDefaultAdminCred()
|
||||
@@ -52,5 +70,5 @@ func getDefaultAdminCred() mcclient.TokenCredential {
|
||||
}
|
||||
|
||||
func GetDefaultClientSession(ctx context.Context, token mcclient.TokenCredential, region, apiVersion string) *mcclient.ClientSession {
|
||||
return defaultClient.NewSession(ctx, region, "", "", token, apiVersion)
|
||||
return getDefaultClient().NewSession(ctx, region, "", "", token, apiVersion)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
@@ -152,10 +153,14 @@ func (manager *SDomainManager) FetchDomainByIdOrName(domain string) (*SDomain, e
|
||||
return nil, err
|
||||
}
|
||||
q := manager.Query().NotEquals("id", api.KeystoneDomainRoot)
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.Equals(q.Field("id"), domain),
|
||||
sqlchemy.Equals(q.Field("name"), domain),
|
||||
))
|
||||
if stringutils2.IsUtf8(domain) {
|
||||
q = q.Equals("name", domain)
|
||||
} else {
|
||||
q = q.Filter(sqlchemy.OR(
|
||||
sqlchemy.Equals(q.Field("id"), domain),
|
||||
sqlchemy.Equals(q.Field("name"), domain),
|
||||
))
|
||||
}
|
||||
err = q.First(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -204,6 +209,11 @@ func (domain *SDomain) GetGroupCount() (int, error) {
|
||||
return q.CountWithError()
|
||||
}
|
||||
|
||||
func (domain *SDomain) GetIdpCount() (int, error) {
|
||||
q := IdentityProviderManager.Query().Equals("target_domain_id", domain.Id)
|
||||
return q.CountWithError()
|
||||
}
|
||||
|
||||
func (domain *SDomain) ValidatePurgeCondition(ctx context.Context) error {
|
||||
if domain.Enabled.IsTrue() {
|
||||
return httperrors.NewInvalidStatusError("domain is enabled")
|
||||
@@ -300,6 +310,12 @@ func domainExtra(domain *SDomain, extra *jsonutils.JSONDict) *jsonutils.JSONDict
|
||||
extra.Add(jsonutils.NewInt(int64(grpCnt)), "group_count")
|
||||
prjCnt, _ := domain.GetProjectCount()
|
||||
extra.Add(jsonutils.NewInt(int64(prjCnt)), "project_count")
|
||||
roleCnt, _ := domain.GetRoleCount()
|
||||
extra.Add(jsonutils.NewInt(int64(roleCnt)), "role_count")
|
||||
policyCnt, _ := domain.GetPolicyCount()
|
||||
extra.Add(jsonutils.NewInt(int64(policyCnt)), "policy_count")
|
||||
idpCnt, _ := domain.GetIdpCount()
|
||||
extra.Add(jsonutils.NewInt(int64(idpCnt)), "idp_count")
|
||||
return extra
|
||||
}
|
||||
|
||||
@@ -371,3 +387,18 @@ func (manager *SDomainManager) FetchCustomizeColumns(ctx context.Context, userCr
|
||||
rows := manager.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields)
|
||||
return expandIdpAttributes(rows, objs, fields, api.IdMappingEntityDomain)
|
||||
}
|
||||
|
||||
func (domain *SDomain) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
domain.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
logclient.AddActionLogWithContext(ctx, domain, logclient.ACT_CREATE, data, userCred, true)
|
||||
}
|
||||
|
||||
func (domain *SDomain) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
domain.SStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
|
||||
logclient.AddActionLogWithContext(ctx, domain, logclient.ACT_UPDATE, data, userCred, true)
|
||||
}
|
||||
|
||||
func (domain *SDomain) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
domain.SStandaloneResourceBase.PostDelete(ctx, userCred)
|
||||
logclient.AddActionLogWithContext(ctx, domain, logclient.ACT_DELETE, nil, userCred, true)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
@@ -322,3 +323,21 @@ func (manager *SEndpointManager) ValidateCreateData(ctx context.Context, userCre
|
||||
}
|
||||
return manager.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, data)
|
||||
}
|
||||
|
||||
func (endpoint *SEndpoint) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
endpoint.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
logclient.AddActionLogWithContext(ctx, endpoint, logclient.ACT_CREATE, data, userCred, true)
|
||||
refreshDefaultClientServiceCatalog()
|
||||
}
|
||||
|
||||
func (endpoint *SEndpoint) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
endpoint.SStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
|
||||
logclient.AddActionLogWithContext(ctx, endpoint, logclient.ACT_UPDATE, data, userCred, true)
|
||||
refreshDefaultClientServiceCatalog()
|
||||
}
|
||||
|
||||
func (endpoint *SEndpoint) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
endpoint.SStandaloneResourceBase.PostDelete(ctx, userCred)
|
||||
logclient.AddActionLogWithContext(ctx, endpoint, logclient.ACT_DELETE, nil, userCred, true)
|
||||
refreshDefaultClientServiceCatalog()
|
||||
}
|
||||
|
||||
@@ -28,19 +28,20 @@ import (
|
||||
|
||||
func expandIdpAttributes(rows []*jsonutils.JSONDict, objs []db.IModel, fields stringutils2.SSortedStrings, entType string) []*jsonutils.JSONDict {
|
||||
if len(fields) == 0 || fields.Contains("idp_id") || fields.Contains("idp") || fields.Contains("idp_entity_id") || fields.Contains("idp_driver") {
|
||||
log.Debugf("objs %d", len(objs))
|
||||
idList := make([]string, len(objs))
|
||||
for i := range objs {
|
||||
idList = append(idList, objs[i].GetId())
|
||||
idList[i] = objs[i].GetId()
|
||||
}
|
||||
idps, err := fetchIdmappings(idList, entType)
|
||||
if err == nil && idps != nil {
|
||||
for i := range rows {
|
||||
if idp, ok := idps[objs[i].GetId()]; ok {
|
||||
if len(fields) == 0 || fields.Contains("idp_id") {
|
||||
rows[i].Set("idp_id", jsonutils.NewString(idp.Id))
|
||||
rows[i].Set("idp_id", jsonutils.NewString(idp.IdpId))
|
||||
}
|
||||
if len(fields) == 0 || fields.Contains("idp") {
|
||||
rows[i].Set("idp", jsonutils.NewString(idp.Name))
|
||||
rows[i].Set("idp", jsonutils.NewString(idp.IdpName))
|
||||
}
|
||||
if len(fields) == 0 || fields.Contains("idp_entity_id") {
|
||||
rows[i].Set("idp_entity_id", jsonutils.NewString(idp.EntityId))
|
||||
@@ -58,8 +59,8 @@ func expandIdpAttributes(rows []*jsonutils.JSONDict, objs []db.IModel, fields st
|
||||
}
|
||||
|
||||
type sIdpInfo struct {
|
||||
Id string
|
||||
Name string
|
||||
IdpId string
|
||||
IdpName string
|
||||
EntityId string
|
||||
Driver string
|
||||
PublicId string
|
||||
@@ -69,13 +70,13 @@ func fetchIdmappings(idList []string, resType string) (map[string]sIdpInfo, erro
|
||||
idmappings := IdmappingManager.Query().SubQuery()
|
||||
idps := IdentityProviderManager.Query().SubQuery()
|
||||
|
||||
q := idmappings.Query(idmappings.Field("domain_id", "id"),
|
||||
q := idmappings.Query(idmappings.Field("domain_id", "idp_id"),
|
||||
idmappings.Field("local_id", "entity_id"),
|
||||
idps.Field("name"),
|
||||
idps.Field("name", "idp_name"),
|
||||
idps.Field("driver"),
|
||||
idmappings.Field("public_id"),
|
||||
).Join(idps, sqlchemy.Equals(idps.Field("id"),
|
||||
idmappings.Field("domain_id")))
|
||||
)
|
||||
q = q.Join(idps, sqlchemy.Equals(idps.Field("id"), idmappings.Field("domain_id")))
|
||||
q = q.Filter(sqlchemy.In(idmappings.Field("public_id"), idList))
|
||||
q = q.Filter(sqlchemy.Equals(idmappings.Field("entity_type"), resType))
|
||||
|
||||
|
||||
@@ -274,3 +274,7 @@ func (manager *SGroupManager) FetchCustomizeColumns(ctx context.Context, userCre
|
||||
rows := manager.SIdentityBaseResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields)
|
||||
return expandIdpAttributes(rows, objs, fields, api.IdMappingEntityGroup)
|
||||
}
|
||||
|
||||
func (manager *SGroupManager) FetchGroupLocalIdsInDomain(domainId string, excludes []string) ([]string, error) {
|
||||
return fetchLocalIdsInDomain(manager, domainId, excludes)
|
||||
}
|
||||
|
||||
@@ -125,10 +125,10 @@ func (manager *SIdmappingManager) FetchEntity(idStr string, entType string) (*SI
|
||||
}
|
||||
|
||||
func (manager *SIdmappingManager) deleteByIdpId(idpId string) error {
|
||||
return manager.DeleteAny(idpId, "", nil)
|
||||
return manager.DeleteAny(idpId, "", nil, nil)
|
||||
}
|
||||
|
||||
func (manager *SIdmappingManager) DeleteAny(idpId string, entityType string, excludeLocalIds []string) error {
|
||||
func (manager *SIdmappingManager) DeleteAny(idpId string, entityType string, excludeLocalIds []string, includeLocalIds []string) error {
|
||||
q := manager.Query().Equals("domain_id", idpId)
|
||||
if len(entityType) > 0 {
|
||||
q = q.Equals("entity_type", entityType)
|
||||
@@ -136,6 +136,9 @@ func (manager *SIdmappingManager) DeleteAny(idpId string, entityType string, exc
|
||||
if len(excludeLocalIds) > 0 {
|
||||
q = q.NotIn("local_id", excludeLocalIds)
|
||||
}
|
||||
if len(includeLocalIds) > 0 {
|
||||
q = q.In("local_id", includeLocalIds)
|
||||
}
|
||||
idmappings := make([]SIdmapping, 0)
|
||||
err := db.FetchModelObjects(manager, q, &idmappings)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
|
||||
@@ -19,10 +19,10 @@ import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/keystone/driver"
|
||||
"yunion.io/x/onecloud/pkg/keystone/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type SIdentityProviderManager struct {
|
||||
@@ -72,6 +73,10 @@ type SIdentityProvider struct {
|
||||
Driver string `width:"32" charset:"ascii" nullable:"false" list:"admin" create:"admin_required"`
|
||||
Template string `width:"32" charset:"ascii" nullable:"true" list:"admin" create:"admin_optional"`
|
||||
|
||||
TargetDomainId string `width:"64" charset:"ascii" nullable:"true" list:"admin" create:"admin_optional" update:"admin"`
|
||||
|
||||
AutoCreateProject tristate.TriState `default:"true" nullable:"true" list:"admin" create:"admin_optional" update:"admin"`
|
||||
|
||||
ErrorCount int `list:"admin"`
|
||||
|
||||
SyncStatus string `width:"10" charset:"ascii" default:"idle" list:"admin"`
|
||||
@@ -299,6 +304,19 @@ func (manager *SIdentityProviderManager) ValidateCreateData(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
targetDomainStr, _ := data.GetString("target_domain")
|
||||
if len(targetDomainStr) > 0 {
|
||||
domain, err := DomainManager.FetchDomainById(targetDomainStr)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, httperrors.NewResourceNotFoundError2(DomainManager.Keyword(), targetDomainStr)
|
||||
} else {
|
||||
return nil, httperrors.NewGeneralError(err)
|
||||
}
|
||||
}
|
||||
data.Set("target_domain_id", jsonutils.NewString(domain.Id))
|
||||
}
|
||||
|
||||
opts := api.TIdentityProviderConfigs{}
|
||||
err := data.Unmarshal(&opts, "config")
|
||||
if err != nil {
|
||||
@@ -315,6 +333,8 @@ func (ident *SIdentityProvider) CustomizeCreate(ctx context.Context, userCred mc
|
||||
func (ident *SIdentityProvider) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
ident.SEnabledStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
|
||||
logclient.AddActionLogWithContext(ctx, ident, logclient.ACT_CREATE, data, userCred, true)
|
||||
|
||||
opts := api.TIdentityProviderConfigs{}
|
||||
err := data.Unmarshal(&opts, "config")
|
||||
if err != nil {
|
||||
@@ -405,6 +425,12 @@ func (self *SIdentityProvider) GetExtraDetails(ctx context.Context, userCred mcc
|
||||
func (self *SIdentityProvider) getMoreDetails(extra *jsonutils.JSONDict) *jsonutils.JSONDict {
|
||||
extra = db.FetchModelExtraCountProperties(self, extra)
|
||||
extra.Set("sync_interval_seconds", jsonutils.NewInt(int64(self.getSyncIntervalSeconds())))
|
||||
if len(self.TargetDomainId) > 0 {
|
||||
domain, _ := DomainManager.FetchDomainById(self.TargetDomainId)
|
||||
if domain != nil {
|
||||
extra.Set("target_domain", jsonutils.NewString(domain.Name))
|
||||
}
|
||||
}
|
||||
return extra
|
||||
}
|
||||
|
||||
|
||||
@@ -19,14 +19,15 @@ import (
|
||||
"database/sql"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/log"
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
@@ -232,3 +233,18 @@ func (self *SEnabledIdentityBaseResource) ValidateDeleteCondition(ctx context.Co
|
||||
}
|
||||
return self.SIdentityBaseResource.ValidateDeleteCondition(ctx)
|
||||
}
|
||||
|
||||
func (model *SIdentityBaseResource) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
model.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
logclient.AddActionLogWithContext(ctx, model, logclient.ACT_CREATE, data, userCred, true)
|
||||
}
|
||||
|
||||
func (model *SIdentityBaseResource) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
model.SStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
|
||||
logclient.AddActionLogWithContext(ctx, model, logclient.ACT_UPDATE, data, userCred, true)
|
||||
}
|
||||
|
||||
func (model *SIdentityBaseResource) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
model.SStandaloneResourceBase.PostDelete(ctx, userCred)
|
||||
logclient.AddActionLogWithContext(ctx, model, logclient.ACT_DELETE, nil, userCred, true)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -29,6 +30,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/pinyinutils"
|
||||
)
|
||||
|
||||
type SProjectManager struct {
|
||||
@@ -312,3 +314,13 @@ func projectExtra(proj *SProject, extra *jsonutils.JSONDict) *jsonutils.JSONDict
|
||||
func (proj *SProject) getExternalResources() (map[string]int, error) {
|
||||
return ProjectResourceManager.getProjectResource(proj.Id)
|
||||
}
|
||||
|
||||
func NormalizeProjectName(name string) string {
|
||||
name = pinyinutils.Text2Pinyin(name)
|
||||
for _, illChar := range []string{
|
||||
"/", ".", " ",
|
||||
} {
|
||||
name = strings.ReplaceAll(name, illChar, "")
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNormalizeProjectName(t *testing.T) {
|
||||
cases := []struct {
|
||||
In string
|
||||
Want string
|
||||
}{
|
||||
{"分公司1", "fengongsi1"},
|
||||
{"集团/分公司/项目A", "jituanfengongsixiangmuA"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := NormalizeProjectName(c.In)
|
||||
if got != c.Want {
|
||||
t.Errorf("NormalizeProjectName %s got %s want %s", c.In, got, c.Want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type SServiceManager struct {
|
||||
@@ -120,3 +121,18 @@ func serviceExtra(service *SService, extra *jsonutils.JSONDict) *jsonutils.JSOND
|
||||
extra.Add(jsonutils.NewInt(int64(epCnt)), "endpoint_count")
|
||||
return extra
|
||||
}
|
||||
|
||||
func (service *SService) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
service.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
|
||||
logclient.AddActionLogWithContext(ctx, service, logclient.ACT_CREATE, data, userCred, true)
|
||||
}
|
||||
|
||||
func (service *SService) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
|
||||
service.SStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
|
||||
logclient.AddActionLogWithContext(ctx, service, logclient.ACT_UPDATE, data, userCred, true)
|
||||
}
|
||||
|
||||
func (service *SService) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
|
||||
service.SStandaloneResourceBase.PostDelete(ctx, userCred)
|
||||
logclient.AddActionLogWithContext(ctx, service, logclient.ACT_DELETE, nil, userCred, true)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func submitIdpSyncTask(ctx context.Context, userCred mcclient.TokenCredential, i
|
||||
idp.MarkDisconnected(ctx, userCred)
|
||||
return
|
||||
}
|
||||
driver, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, conf)
|
||||
driver, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, idp.AutoCreateProject.Bool(), conf)
|
||||
if err != nil {
|
||||
log.Errorf("GetDriver for idp %s fail %s", idp.Name, err)
|
||||
idp.MarkDisconnected(ctx, userCred)
|
||||
|
||||
@@ -656,3 +656,37 @@ func (manager *SUserManager) FetchCustomizeColumns(ctx context.Context, userCred
|
||||
rows := manager.SEnabledIdentityBaseResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields)
|
||||
return expandIdpAttributes(rows, objs, fields, api.IdMappingEntityUser)
|
||||
}
|
||||
|
||||
func (manager *SUserManager) FetchUserLocalIdsInDomain(domainId string, excludes []string) ([]string, error) {
|
||||
return fetchLocalIdsInDomain(manager, domainId, excludes)
|
||||
}
|
||||
|
||||
func fetchLocalIdsInDomain(manager db.IModelManager, domainId string, excludes []string) ([]string, error) {
|
||||
idmappings := IdmappingManager.Query().SubQuery()
|
||||
users := manager.Query().SubQuery()
|
||||
q := idmappings.Query(idmappings.Field("local_id"))
|
||||
q = q.Join(users, sqlchemy.AND(
|
||||
sqlchemy.Equals(idmappings.Field("entity_type"), manager.Keyword()),
|
||||
sqlchemy.Equals(idmappings.Field("public_id"), users.Field("id")),
|
||||
))
|
||||
q = q.Filter(sqlchemy.Equals(users.Field("domain_id"), domainId))
|
||||
q = q.Filter(sqlchemy.NotIn(idmappings.Field("local_id"), excludes))
|
||||
rows, err := q.Rows()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, errors.Wrap(err, "query")
|
||||
}
|
||||
if rows == nil {
|
||||
return nil, nil
|
||||
}
|
||||
defer rows.Close()
|
||||
ret := make([]string, 0)
|
||||
for rows.Next() {
|
||||
var idStr string
|
||||
err = rows.Scan(&idStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "scan")
|
||||
}
|
||||
ret = append(ret, idStr)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -20,9 +20,11 @@ import (
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/keystone/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type IdentityProviderDeleteTask struct {
|
||||
@@ -38,9 +40,12 @@ func (self *IdentityProviderDeleteTask) OnInit(ctx context.Context, obj db.IStan
|
||||
|
||||
err := idp.Purge(ctx, self.UserCred)
|
||||
if err != nil {
|
||||
idp.SetStatus(self.UserCred, api.IdentityDriverStatusDeleteFailed, err.Error())
|
||||
self.SetStageFailed(ctx, fmt.Sprintf("purge failed %s", err))
|
||||
logclient.AddActionLogWithStartable(self, idp, logclient.ACT_DELETE, err, self.UserCred, false)
|
||||
return
|
||||
}
|
||||
|
||||
logclient.AddActionLogWithStartable(self, idp, logclient.ACT_DELETE, nil, self.UserCred, true)
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func authUserByIdentity(ctx context.Context, ident mcclient.SAuthenticationIdent
|
||||
return nil, errors.Wrap(err, "GetConfig")
|
||||
}
|
||||
|
||||
backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, conf)
|
||||
backend, err := driver.GetDriver(idp.Driver, idp.Id, idp.Name, idp.Template, idp.TargetDomainId, idp.AutoCreateProject.Bool(), conf)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "driver.GetDriver")
|
||||
}
|
||||
|
||||
@@ -93,6 +93,10 @@ func (this *Client) AuthVersion() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Client) SetServiceCatalog(catalog IServiceCatalog) {
|
||||
this.serviceCatalog = catalog
|
||||
}
|
||||
|
||||
func (this *Client) NewAuthTokenCredential() TokenCredential {
|
||||
if this.AuthVersion() == "v3" {
|
||||
return &TokenCredentialV3{}
|
||||
|
||||
@@ -15,18 +15,23 @@
|
||||
package ldaputils
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/ldap.v3"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUserNotFound = errors.New("not found")
|
||||
ErrUserDuplicate = errors.New("user id duplicate")
|
||||
ErrUserBadCredential = errors.New("bad credential")
|
||||
|
||||
binaryAttributes = []string{
|
||||
"objectGUID",
|
||||
}
|
||||
)
|
||||
|
||||
type SLDAPClient struct {
|
||||
@@ -114,6 +119,9 @@ func (cli *SLDAPClient) Search(base string, objClass string, condition map[strin
|
||||
searches.WriteString("(")
|
||||
searches.WriteString(k)
|
||||
searches.WriteString("=")
|
||||
if isBinaryAttr(k) {
|
||||
v = toBinary(v)
|
||||
}
|
||||
searches.WriteString(v)
|
||||
searches.WriteString(")")
|
||||
}
|
||||
@@ -144,3 +152,43 @@ func (cli *SLDAPClient) Search(base string, objClass string, condition map[strin
|
||||
|
||||
return sr.Entries, nil
|
||||
}
|
||||
|
||||
func isBinaryAttr(attrName string) bool {
|
||||
for _, attr := range binaryAttributes {
|
||||
if strings.EqualFold(attr, attrName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func toBinary(val string) string {
|
||||
ret, err := hex.DecodeString(val)
|
||||
if err != nil {
|
||||
return val
|
||||
} else {
|
||||
return string(ret)
|
||||
}
|
||||
}
|
||||
|
||||
func toHex(val string) string {
|
||||
return hex.EncodeToString([]byte(val))
|
||||
}
|
||||
|
||||
func GetAttributeValue(e *ldap.Entry, key string) string {
|
||||
val := e.GetAttributeValue(key)
|
||||
if isBinaryAttr(key) {
|
||||
val = toHex(val)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func GetAttributeValues(e *ldap.Entry, key string) []string {
|
||||
vals := e.GetAttributeValues(key)
|
||||
if isBinaryAttr(key) {
|
||||
for i := range vals {
|
||||
vals[i] = toHex(vals[i])
|
||||
}
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pinyinutils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/mozillazg/go-pinyin"
|
||||
)
|
||||
|
||||
func Text2Pinyin(hans string) string {
|
||||
args := pinyin.NewArgs()
|
||||
out := strings.Builder{}
|
||||
for _, runeVal := range hans {
|
||||
if runeVal > 0x7f {
|
||||
o := pinyin.SinglePinyin(runeVal, args)
|
||||
for i := range o {
|
||||
out.WriteString(o[i])
|
||||
}
|
||||
} else {
|
||||
out.WriteRune(runeVal)
|
||||
}
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pinyinutils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPinyin(t *testing.T) {
|
||||
cases := []struct {
|
||||
In string
|
||||
Want string
|
||||
}{
|
||||
{"中国人", "zhongguoren"},
|
||||
{"中国人1", "zhongguoren1"},
|
||||
{"中 国 人", "zhong guo ren"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := Text2Pinyin(c.In)
|
||||
if got != c.Want {
|
||||
t.Errorf("Text2Pinyin %s got: %s want %s", c.In, got, c.Want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package stringutils2
|
||||
|
||||
func IsUtf8(str string) bool {
|
||||
for _, runeVal := range str {
|
||||
if runeVal > 0x7f {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package stringutils2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsUtf8(t *testing.T) {
|
||||
cases := []struct {
|
||||
In string
|
||||
Want bool
|
||||
}{
|
||||
{"中文", true},
|
||||
{"this is eng", false},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := IsUtf8(c.In)
|
||||
if got != c.Want {
|
||||
t.Errorf("IsUtf8 %s got %v want %v", c.In, got, c.Want)
|
||||
}
|
||||
}
|
||||
}
|
||||
+155
-59
@@ -29,6 +29,15 @@ const (
|
||||
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
||||
)
|
||||
|
||||
const (
|
||||
genericRead = 0x80000000
|
||||
genericWrite = 0x40000000
|
||||
)
|
||||
|
||||
const (
|
||||
consoleTextmodeBuffer = 0x1
|
||||
)
|
||||
|
||||
type wchar uint16
|
||||
type short int16
|
||||
type dword uint32
|
||||
@@ -69,14 +78,17 @@ var (
|
||||
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
|
||||
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
|
||||
procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW")
|
||||
procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer")
|
||||
)
|
||||
|
||||
// Writer provide colorable Writer to the console
|
||||
type Writer struct {
|
||||
out io.Writer
|
||||
handle syscall.Handle
|
||||
oldattr word
|
||||
oldpos coord
|
||||
out io.Writer
|
||||
handle syscall.Handle
|
||||
althandle syscall.Handle
|
||||
oldattr word
|
||||
oldpos coord
|
||||
rest bytes.Buffer
|
||||
}
|
||||
|
||||
// NewColorable return new instance of Writer which handle escape sequence from File.
|
||||
@@ -407,7 +419,18 @@ func (w *Writer) Write(data []byte) (n int, err error) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
er := bytes.NewReader(data)
|
||||
handle := w.handle
|
||||
|
||||
var er *bytes.Reader
|
||||
if w.rest.Len() > 0 {
|
||||
var rest bytes.Buffer
|
||||
w.rest.WriteTo(&rest)
|
||||
w.rest.Reset()
|
||||
rest.Write(data)
|
||||
er = bytes.NewReader(rest.Bytes())
|
||||
} else {
|
||||
er = bytes.NewReader(data)
|
||||
}
|
||||
var bw [1]byte
|
||||
loop:
|
||||
for {
|
||||
@@ -425,29 +448,55 @@ loop:
|
||||
break loop
|
||||
}
|
||||
|
||||
if c2 == ']' {
|
||||
if err := doTitleSequence(er); err != nil {
|
||||
switch c2 {
|
||||
case '>':
|
||||
continue
|
||||
case ']':
|
||||
w.rest.WriteByte(c1)
|
||||
w.rest.WriteByte(c2)
|
||||
er.WriteTo(&w.rest)
|
||||
if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 {
|
||||
break loop
|
||||
}
|
||||
continue
|
||||
}
|
||||
if c2 != 0x5b {
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
var m byte
|
||||
for {
|
||||
c, err := er.ReadByte()
|
||||
er = bytes.NewReader(w.rest.Bytes()[2:])
|
||||
err := doTitleSequence(er)
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
w.rest.Reset()
|
||||
continue
|
||||
// https://github.com/mattn/go-colorable/issues/27
|
||||
case '7':
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
w.oldpos = csbi.cursorPosition
|
||||
continue
|
||||
case '8':
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
||||
continue
|
||||
case 0x5b:
|
||||
// execute part after switch
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
w.rest.WriteByte(c1)
|
||||
w.rest.WriteByte(c2)
|
||||
er.WriteTo(&w.rest)
|
||||
|
||||
var buf bytes.Buffer
|
||||
var m byte
|
||||
for i, c := range w.rest.Bytes()[2:] {
|
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||
m = c
|
||||
er = bytes.NewReader(w.rest.Bytes()[2+i+1:])
|
||||
w.rest.Reset()
|
||||
break
|
||||
}
|
||||
buf.Write([]byte(string(c)))
|
||||
}
|
||||
if m == 0 {
|
||||
break loop
|
||||
}
|
||||
|
||||
switch m {
|
||||
case 'A':
|
||||
@@ -455,61 +504,64 @@ loop:
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.y -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'B':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.y += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'C':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'D':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
if csbi.cursorPosition.x < 0 {
|
||||
csbi.cursorPosition.x = 0
|
||||
}
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'E':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x = 0
|
||||
csbi.cursorPosition.y += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'F':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x = 0
|
||||
csbi.cursorPosition.y -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'G':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x = short(n - 1)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'H', 'f':
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
if buf.Len() > 0 {
|
||||
token := strings.Split(buf.String(), ";")
|
||||
switch len(token) {
|
||||
@@ -534,7 +586,7 @@ loop:
|
||||
} else {
|
||||
csbi.cursorPosition.y = 0
|
||||
}
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'J':
|
||||
n := 0
|
||||
if buf.Len() > 0 {
|
||||
@@ -545,20 +597,20 @@ loop:
|
||||
}
|
||||
var count, written dword
|
||||
var cursor coord
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
switch n {
|
||||
case 0:
|
||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
||||
count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x)
|
||||
case 1:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.window.top-csbi.cursorPosition.y)*csbi.size.x)
|
||||
count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x)
|
||||
case 2:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
||||
count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x)
|
||||
}
|
||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
case 'K':
|
||||
n := 0
|
||||
if buf.Len() > 0 {
|
||||
@@ -567,28 +619,28 @@ loop:
|
||||
continue
|
||||
}
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
var cursor coord
|
||||
var count, written dword
|
||||
switch n {
|
||||
case 0:
|
||||
cursor = coord{x: csbi.cursorPosition.x + 1, y: csbi.cursorPosition.y}
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x - 1)
|
||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
||||
case 1:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||
cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y}
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
||||
case 2:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||
cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y}
|
||||
count = dword(csbi.size.x)
|
||||
}
|
||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
case 'm':
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
attr := csbi.attributes
|
||||
cs := buf.String()
|
||||
if cs == "" {
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
|
||||
procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr))
|
||||
continue
|
||||
}
|
||||
token := strings.Split(cs, ";")
|
||||
@@ -627,6 +679,21 @@ loop:
|
||||
attr |= n256foreAttr[n256]
|
||||
i += 2
|
||||
}
|
||||
} else if len(token) == 5 && token[i+1] == "2" {
|
||||
var r, g, b int
|
||||
r, _ = strconv.Atoi(token[i+2])
|
||||
g, _ = strconv.Atoi(token[i+3])
|
||||
b, _ = strconv.Atoi(token[i+4])
|
||||
i += 4
|
||||
if r > 127 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if g > 127 {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if b > 127 {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
} else {
|
||||
attr = attr & (w.oldattr & backgroundMask)
|
||||
}
|
||||
@@ -654,6 +721,21 @@ loop:
|
||||
attr |= n256backAttr[n256]
|
||||
i += 2
|
||||
}
|
||||
} else if len(token) == 5 && token[i+1] == "2" {
|
||||
var r, g, b int
|
||||
r, _ = strconv.Atoi(token[i+2])
|
||||
g, _ = strconv.Atoi(token[i+3])
|
||||
b, _ = strconv.Atoi(token[i+4])
|
||||
i += 4
|
||||
if r > 127 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if g > 127 {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if b > 127 {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
} else {
|
||||
attr = attr & (w.oldattr & foregroundMask)
|
||||
}
|
||||
@@ -685,38 +767,52 @@ loop:
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
}
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
||||
procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr))
|
||||
}
|
||||
}
|
||||
case 'h':
|
||||
var ci consoleCursorInfo
|
||||
cs := buf.String()
|
||||
if cs == "5>" {
|
||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||
ci.visible = 0
|
||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||
} else if cs == "?25" {
|
||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||
ci.visible = 1
|
||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||
} else if cs == "?1049" {
|
||||
if w.althandle == 0 {
|
||||
h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0)
|
||||
w.althandle = syscall.Handle(h)
|
||||
if w.althandle != 0 {
|
||||
handle = w.althandle
|
||||
}
|
||||
}
|
||||
}
|
||||
case 'l':
|
||||
var ci consoleCursorInfo
|
||||
cs := buf.String()
|
||||
if cs == "5>" {
|
||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||
ci.visible = 1
|
||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||
} else if cs == "?25" {
|
||||
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||
ci.visible = 0
|
||||
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||
procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci)))
|
||||
} else if cs == "?1049" {
|
||||
if w.althandle != 0 {
|
||||
syscall.CloseHandle(w.althandle)
|
||||
w.althandle = 0
|
||||
handle = w.handle
|
||||
}
|
||||
}
|
||||
case 's':
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
w.oldpos = csbi.cursorPosition
|
||||
case 'u':
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
||||
procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module github.com/mattn/go-colorable
|
||||
|
||||
require github.com/mattn/go-isatty v0.0.8
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module github.com/mattn/go-isatty
|
||||
|
||||
require golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
Generated
Vendored
+7
-2
@@ -1,5 +1,4 @@
|
||||
// +build linux
|
||||
// +build !appengine,!ppc64,!ppc64le
|
||||
// +build android
|
||||
|
||||
package isatty
|
||||
|
||||
@@ -16,3 +15,9 @@ func IsTerminal(fd uintptr) bool {
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
// +build appengine
|
||||
|
||||
package isatty
|
||||
|
||||
// IsTerminal returns true if the file descriptor is terminal which
|
||||
// is always false on on appengine classic which is a sandboxed PaaS.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
+6
@@ -16,3 +16,9 @@ func IsTerminal(fd uintptr) bool {
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
// +build linux
|
||||
// +build ppc64 ppc64le
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
syscall "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = syscall.TCGETS
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var termios syscall.Termios
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
||||
+8
-3
@@ -1,9 +1,14 @@
|
||||
// +build !windows
|
||||
// +build !appengine
|
||||
// +build appengine js nacl
|
||||
|
||||
package isatty
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// IsTerminal returns true if the file descriptor is terminal which
|
||||
// is always false on js and appengine classic which is a sandboxed PaaS.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
|
||||
+6
@@ -14,3 +14,9 @@ func IsTerminal(fd uintptr) bool {
|
||||
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// +build linux aix
|
||||
// +build !appengine
|
||||
// +build !android
|
||||
|
||||
package isatty
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Indentiation
|
||||
[*.{py,rst}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
[{Makefile,*.go}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
[*.{ini,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
*~
|
||||
*.swp
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "_tools/pinyin-data"]
|
||||
path = _tools/pinyin-data
|
||||
url = https://github.com/mozillazg/pinyin-data.git
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
language: go
|
||||
go:
|
||||
- '1.7'
|
||||
- '1.8'
|
||||
- '1.9.x'
|
||||
- '1.10.x'
|
||||
- '1.11.x'
|
||||
- '1.12.x'
|
||||
- 'master'
|
||||
|
||||
sudo: false
|
||||
|
||||
before_install:
|
||||
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||
- go get github.com/mattn/go-isatty
|
||||
- go get github.com/axw/gocov/gocov
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
- go run cmd/pinyin/main.go abc
|
||||
- go run cmd/pinyin/main.go -s zhao abc
|
||||
- echo "abc" | go run cmd/pinyin/main.go
|
||||
- echo "abc" > abc.txt && go run cmd/pinyin/main.go < abc.txt
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci -v -package .
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
# Changelog
|
||||
|
||||
## [0.15.0] (2019-04-06)
|
||||
|
||||
* **Changed** 使用 [pinyin-data][pinyin-data] v0.7.0 的拼音数据
|
||||
* **NEW** 添加 go.mod 文件
|
||||
|
||||
|
||||
## [0.14.0] (2018-08-05)
|
||||
|
||||
* **Changed** 使用 [pinyin-data][pinyin-data] v0.6.1 的拼音数据
|
||||
* **Changed** 命令行工具移到 `cmd/pinyin/` 目录下,现在需要改为使用
|
||||
`go get -u github.com/mozillazg/go-pinyin/cmd/pinyin` 来安装命令行工具。
|
||||
|
||||
|
||||
## [0.13.0] (2018-04-29)
|
||||
|
||||
* **Changed** 使用 [pinyin-data][pinyin-data] v0.5.1 的拼音数据 (via [#30])
|
||||
* **Changed** 修改命令行工具 `-s` 参数的值(thanks [@wdscxsj][@wdscxsj] via [#19][#19]):
|
||||
* `Normal` 改为 `zhao`
|
||||
* `Tone` 改为 `zh4ao`
|
||||
* `Tone2` 改为 `zha4o`
|
||||
* `Tone3` 改为 `zhao4`
|
||||
* `Initials` 改为 `zh`
|
||||
* `FirstLetter` 改为 `z`
|
||||
* `Finals` 改为 `ao`
|
||||
* `FinalsTone` 改为 `4ao`
|
||||
* `FinalsTone2` 改为 `a4o`
|
||||
* `FinalsTone3` 改为 `ao4`
|
||||
* **Changed** 严格限制命令行参数中 `-s` 选项的值(thanks [@wdscxsj][@wdscxsj] via [#20][#20]):
|
||||
|
||||
|
||||
## [0.12.0] (2017-04-25)
|
||||
|
||||
|
||||
* **NEW** 命令行程序支持通过 -s 指定新增的 `Tone3` 和 `FinalsTone3` 拼音风格
|
||||
|
||||
$ pinyin -s Tone3 请至少输入一个汉字
|
||||
qing3 zhi4 shao3 shu1 ru4 yi1 ge4 han4 zi4
|
||||
|
||||
$ pinyin -s FinalsTone3 请至少输入一个汉字
|
||||
ing3 i4 ao3 u1 u4 i1 e4 an4 i4
|
||||
|
||||
* **Changed** use [pinyin-data](https://github.com/mozillazg/pinyin-data) v0.4.1
|
||||
|
||||
|
||||
## [0.11.0] (2016-10-28)
|
||||
|
||||
* **Changed** 不再使用 `0` 表示轻声(因为之前并没有正确的实现这个功能, 同时也觉得这个功能没必要)。
|
||||
顺便修复了 Tone2 中 `ü` 标轻声的问题(像 `侵略 -> qi1n lv0e4` )
|
||||
* **NEW** 新增 `Tone3` 和 `FinalsTone3` 拼音风格。
|
||||
|
||||
hans := "中国人"
|
||||
args := pinyin.NewArgs()
|
||||
args.Style = pinyin.Tone3
|
||||
fmt.Println("Tone3:", pinyin.Pinyin(hans, args))
|
||||
// Output: Tone3: [[zhong1] [guo2] [ren2]]
|
||||
|
||||
args.Style = pinyin.FinalsTone3
|
||||
fmt.Println("FinalsTone3:", pinyin.Pinyin(hans, args))
|
||||
// Output: FinalsTone3: [[ong1] [uo2] [en2]]
|
||||
|
||||
|
||||
|
||||
## [0.10.0] (2016-10-18)
|
||||
|
||||
* **Changed** use [pinyin-data](https://github.com/mozillazg/pinyin-data) v0.4.0
|
||||
|
||||
|
||||
## [0.9.0] (2016-09-04):
|
||||
|
||||
* **NEW** 新增 `func Convert(s string, a *Args) [][]string`
|
||||
* **NEW** 新增 `func LazyConvert(s string, a *Args) []string`
|
||||
|
||||
之所以增加这两个函数是希望 `a` 参数支持 `nil`
|
||||
|
||||
|
||||
|
||||
## [0.8.0] (2016-08-19)
|
||||
|
||||
* **Changed** use [pinyin-data](https://github.com/mozillazg/pinyin-data) v0.3.0
|
||||
* Fixed [#13](https://github.com/mozillazg/go-pinyin/issues/13) . thanks [@aisq2008](https://github.com/aisq2008)
|
||||
* Fixed pinyin of 罗
|
||||
|
||||
|
||||
## [0.7.0] (2016-08-02)
|
||||
|
||||
* **Changed** use [pinyin-data](https://github.com/mozillazg/pinyin-data) v0.2.0
|
||||
* **Improved** golint and gofmt
|
||||
|
||||
|
||||
## [0.6.0] (2016-05-14)
|
||||
|
||||
* **NEW** 命令行程序支持指定拼音风格:
|
||||
|
||||
```shell
|
||||
$ pinyin -s Normal 你好
|
||||
ni hao
|
||||
```
|
||||
* **Bugfixed** 解决韵母 i, u, ü 的问题:根据以下拼音方案,还原出正确的韵母
|
||||
[#8](https://github.com/mozillazg/go-pinyin/pull/8), [python-pinyin#26](https://github.com/mozillazg/python-pinyin/pull/26)
|
||||
|
||||
> i 行的韵母,前面没有声母的时候,写成:yi(衣),yɑ(呀),ye(耶),
|
||||
> yɑo(腰),you(忧),yɑn(烟),yin(因),yɑnɡ(央),yinɡ(英),yonɡ(雍)。
|
||||
>
|
||||
> u 行的韵母,前面没有声母的时候,写成wu(乌),wɑ(蛙),wo(窝),
|
||||
> wɑi(歪),wei(威),wɑn(弯),wen(温),wɑnɡ(汪),wenɡ(翁)。
|
||||
>
|
||||
> ü行的韵母跟声母j,q,x拼的时候,写成ju(居),qu(区),xu(虚),
|
||||
> ü上两点也省略;但是跟声母l,n拼的时候,仍然写成lü(吕),nü(女)。
|
||||
|
||||
**注意** `y` 既不是声母也不是韵母。详见 [汉语拼音方案](http://www.edu.cn/20011114/3009777.shtml)
|
||||
|
||||
* **Bugfixed** 解决未正确处理鼻音 ḿ, ń, ň, ǹ 的问题:包含鼻音的拼音不应该有声母
|
||||
|
||||
|
||||
|
||||
## [0.5.0] (2016-03-12)
|
||||
|
||||
* **CHANGE** 改为使用来自 [pinyin-data](https://github.com/mozillazg/pinyin-data) 的拼音数据。
|
||||
* **NEW** 命令行程序支持从标准输入读取数据(支持管道和重定向输入):
|
||||
|
||||
```shell
|
||||
$ echo "你好" | pinyin
|
||||
nǐ hǎo
|
||||
$ pinyin < hello.txt
|
||||
nǐ hǎo
|
||||
```
|
||||
|
||||
|
||||
## [0.4.0] (2016-01-29)
|
||||
|
||||
* **NEW** `Args` 结构体新增 field: `Fallback func(r rune, a Args) []string`
|
||||
用于处理没有拼音的字符(默认忽略没有拼音的字符):
|
||||
```go
|
||||
a := pinyin.NewArgs()
|
||||
a.Fallback = func(r rune, a pinyin.Args) []string {
|
||||
return []string{string(r + 1)}
|
||||
}
|
||||
fmt.Println(pinyin.Pinyin("中国人abc", a))
|
||||
// Output: [[zhong] [guo] [ren] [b] [c] [d]]
|
||||
|
||||
// or
|
||||
pinyin.Fallback = func(r rune, a pinyin.Args) []string {
|
||||
return []string{string(r)}
|
||||
}
|
||||
fmt.Println(pinyin.Pinyin("中国人abc", pinyin.NewArgs()))
|
||||
// Output: [[zhong] [guo] [ren] [a] [b] [c]]
|
||||
```
|
||||
|
||||
|
||||
## [0.3.0] (2015-12-29)
|
||||
|
||||
* fix "当字符串中有非中文的时候,会出现下标越界的情况"(影响 `pinyin.LazyPinyin` 和 `pinyin.Slug` ([#1](https://github.com/mozillazg/go-pinyin/issues/1)))
|
||||
* 调整对非中文字符的处理:当遇到没有拼音的字符时,直接忽略
|
||||
```go
|
||||
// before
|
||||
fmt.Println(pinyin.Pinyin("中国人abc", pinyin.NewArgs()))
|
||||
[[zhong] [guo] [ren] [] [] []]
|
||||
|
||||
// after
|
||||
fmt.Println(pinyin.Pinyin("中国人abc", pinyin.NewArgs()))
|
||||
[[zhong] [guo] [ren]]
|
||||
```
|
||||
|
||||
|
||||
## [0.2.1] (2015-08-26)
|
||||
|
||||
* `yu`, `y`, `w` 不是声母
|
||||
|
||||
|
||||
## [0.2.0] (2015-01-04)
|
||||
|
||||
* 新增 `func NewArgs() Args`
|
||||
* 解决 `Args.Separator` 无法赋值为 `""` 的 BUG
|
||||
* 规范命名:
|
||||
* `NORMAL` -> `Normal`
|
||||
* `TONE` -> `Tone`
|
||||
* `TONE2` -> `Tone2`
|
||||
* `INITIALS` -> `Initials`
|
||||
* `FIRST_LETTER` -> `FirstLetter`
|
||||
* `FINALS` -> `Finals`
|
||||
* `FINALS_TONE` -> `FinalsTone`
|
||||
* `FINALS_TONE2` -> `FinalsTone2`
|
||||
|
||||
## [0.1.1] (2014-12-07)
|
||||
* 更新拼音库
|
||||
|
||||
|
||||
## 0.1.0 (2014-11-23)
|
||||
* Initial Release
|
||||
|
||||
|
||||
[pinyin-data]: https://github.com/mozillazg/pinyin-data
|
||||
[@wdscxsj]: https://github.com/wdscxsj
|
||||
[#19]: https://github.com/mozillazg/go-pinyin/pull/19
|
||||
[#20]: https://github.com/mozillazg/go-pinyin/pull/20
|
||||
[#30]: https://github.com/mozillazg/go-pinyin/pull/30
|
||||
|
||||
[0.1.1]: https://github.com/mozillazg/go-pinyin/compare/v0.1.0...v0.1.1
|
||||
[0.2.0]: https://github.com/mozillazg/go-pinyin/compare/v0.1.1...v0.2.0
|
||||
[0.2.1]: https://github.com/mozillazg/go-pinyin/compare/v0.2.0...v0.2.1
|
||||
[0.3.0]: https://github.com/mozillazg/go-pinyin/compare/v0.2.1...v0.3.0
|
||||
[0.4.0]: https://github.com/mozillazg/go-pinyin/compare/v0.3.0...v0.4.0
|
||||
[0.5.0]: https://github.com/mozillazg/go-pinyin/compare/v0.4.0...v0.5.0
|
||||
[0.6.0]: https://github.com/mozillazg/go-pinyin/compare/v0.5.0...v0.6.0
|
||||
[0.7.0]: https://github.com/mozillazg/go-pinyin/compare/v0.6.0...v0.7.0
|
||||
[0.8.0]: https://github.com/mozillazg/go-pinyin/compare/v0.7.0...v0.8.0
|
||||
[0.9.0]: https://github.com/mozillazg/go-pinyin/compare/v0.8.0...v0.9.0
|
||||
[0.10.0]: https://github.com/mozillazg/go-pinyin/compare/v0.9.0...v0.10.0
|
||||
[0.11.0]: https://github.com/mozillazg/go-pinyin/compare/v0.10.0...v0.11.0
|
||||
[0.12.0]: https://github.com/mozillazg/go-pinyin/compare/v0.11.0...v0.12.0
|
||||
[0.13.0]: https://github.com/mozillazg/go-pinyin/compare/v0.12.0...v0.13.0
|
||||
[0.14.0]: https://github.com/mozillazg/go-pinyin/compare/v0.13.0...v0.14.0
|
||||
[0.15.0]: https://github.com/mozillazg/go-pinyin/compare/v0.14.0...v0.15.0
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 mozillazg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
help:
|
||||
@echo "test run test"
|
||||
@echo "lint run lint"
|
||||
@echo "gen_pinyin_dict gen pinyin dict"
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@echo "run test"
|
||||
@go test -v -cover
|
||||
|
||||
.PHONY: gen_pinyin_dict
|
||||
gen_pinyin_dict:
|
||||
@go run _tools/gen_pinyin_dict.go _tools/pinyin-data/pinyin.txt pinyin_dict.go
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
gofmt -s -w . cmd/pinyin _tools
|
||||
golint .
|
||||
golint cmd/pinyin
|
||||
golint _tools
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
go-pinyin
|
||||
=========
|
||||
|
||||
[](https://travis-ci.org/mozillazg/go-pinyin)
|
||||
[](https://coveralls.io/r/mozillazg/go-pinyin?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/mozillazg/go-pinyin)
|
||||
[](https://godoc.org/github.com/mozillazg/go-pinyin)
|
||||
|
||||
汉语拼音转换工具 Go 版。
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
```
|
||||
go get -u github.com/mozillazg/go-pinyin
|
||||
```
|
||||
|
||||
install CLI tool:
|
||||
|
||||
```
|
||||
go get -u github.com/mozillazg/go-pinyin/cmd/pinyin
|
||||
$ pinyin 中国人
|
||||
zhōng guó rén
|
||||
```
|
||||
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
|
||||
API documentation can be found here:
|
||||
https://godoc.org/github.com/mozillazg/go-pinyin
|
||||
|
||||
|
||||
Usage
|
||||
------
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mozillazg/go-pinyin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hans := "中国人"
|
||||
|
||||
// 默认
|
||||
a := pinyin.NewArgs()
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zhong] [guo] [ren]]
|
||||
|
||||
// 包含声调
|
||||
a.Style = pinyin.Tone
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zhōng] [guó] [rén]]
|
||||
|
||||
// 声调用数字表示
|
||||
a.Style = pinyin.Tone2
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zho1ng] [guo2] [re2n]]
|
||||
|
||||
// 开启多音字模式
|
||||
a = pinyin.NewArgs()
|
||||
a.Heteronym = true
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zhong zhong] [guo] [ren]]
|
||||
a.Style = pinyin.Tone2
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zho1ng zho4ng] [guo2] [re2n]]
|
||||
|
||||
fmt.Println(pinyin.LazyPinyin(hans, pinyin.NewArgs()))
|
||||
// [zhong guo ren]
|
||||
|
||||
fmt.Println(pinyin.Convert(hans, nil))
|
||||
// [[zhong] [guo] [ren]]
|
||||
|
||||
fmt.Println(pinyin.LazyConvert(hans, nil))
|
||||
// [zhong guo ren]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Related Projects
|
||||
-----------------
|
||||
|
||||
* [hotoo/pinyin](https://github.com/hotoo/pinyin): 汉语拼音转换工具 Node.js/JavaScript 版。
|
||||
* [mozillazg/python-pinyin](https://github.com/mozillazg/python-pinyin): 汉语拼音转换工具 Python 版。
|
||||
* [mozillazg/rust-pinyin](https://github.com/mozillazg/rust-pinyin): 汉语拼音转换工具 Rust 版。
|
||||
|
||||
|
||||
pinyin data
|
||||
-----------------
|
||||
|
||||
* 使用 [pinyin-data](https://github.com/mozillazg/pinyin-data) 的拼音数据
|
||||
|
||||
|
||||
License
|
||||
---------
|
||||
|
||||
Under the MIT License.
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Package pinyin : 汉语拼音转换工具.
|
||||
|
||||
Usage
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mozillazg/go-pinyin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
hans := "中国人"
|
||||
// 默认
|
||||
a := pinyin.NewArgs()
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zhong] [guo] [ren]]
|
||||
|
||||
// 包含声调
|
||||
a.Style = pinyin.Tone
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zhōng] [guó] [rén]]
|
||||
|
||||
// 声调用数字表示
|
||||
a.Style = pinyin.Tone2
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zho1ng] [guo2] [re2n]]
|
||||
|
||||
// 开启多音字模式
|
||||
a = pinyin.NewArgs()
|
||||
a.Heteronym = true
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zhong zhong] [guo] [ren]]
|
||||
a.Style = pinyin.Tone2
|
||||
fmt.Println(pinyin.Pinyin(hans, a))
|
||||
// [[zho1ng zho4ng] [guo2] [re2n]]
|
||||
}
|
||||
*/
|
||||
package pinyin
|
||||
+1
@@ -0,0 +1 @@
|
||||
module github.com/mozillazg/go-pinyin
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package pinyin
|
||||
|
||||
// 带音标字符。
|
||||
var phoneticSymbol = map[string]string{
|
||||
"ā": "a1",
|
||||
"á": "a2",
|
||||
"ǎ": "a3",
|
||||
"à": "a4",
|
||||
"ē": "e1",
|
||||
"é": "e2",
|
||||
"ě": "e3",
|
||||
"è": "e4",
|
||||
"ō": "o1",
|
||||
"ó": "o2",
|
||||
"ǒ": "o3",
|
||||
"ò": "o4",
|
||||
"ī": "i1",
|
||||
"í": "i2",
|
||||
"ǐ": "i3",
|
||||
"ì": "i4",
|
||||
"ū": "u1",
|
||||
"ú": "u2",
|
||||
"ǔ": "u3",
|
||||
"ù": "u4",
|
||||
"ü": "v",
|
||||
"ǘ": "v2",
|
||||
"ǚ": "v3",
|
||||
"ǜ": "v4",
|
||||
"ń": "n2",
|
||||
"ň": "n3",
|
||||
"ǹ": "n4",
|
||||
"ḿ": "m2",
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
package pinyin
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Meta
|
||||
const (
|
||||
Version = "0.15.0"
|
||||
Author = "mozillazg, 闲耘"
|
||||
License = "MIT"
|
||||
Copyright = "Copyright (c) 2016 mozillazg, 闲耘"
|
||||
)
|
||||
|
||||
// 拼音风格(推荐)
|
||||
const (
|
||||
Normal = 0 // 普通风格,不带声调(默认风格)。如: zhong guo
|
||||
Tone = 1 // 声调风格1,拼音声调在韵母第一个字母上。如: zhōng guó
|
||||
Tone2 = 2 // 声调风格2,即拼音声调在各个韵母之后,用数字 [1-4] 进行表示。如: zho1ng guo2
|
||||
Tone3 = 8 // 声调风格3,即拼音声调在各个拼音之后,用数字 [1-4] 进行表示。如: zhong1 guo2
|
||||
Initials = 3 // 声母风格,只返回各个拼音的声母部分。如: zh g
|
||||
FirstLetter = 4 // 首字母风格,只返回拼音的首字母部分。如: z g
|
||||
Finals = 5 // 韵母风格,只返回各个拼音的韵母部分,不带声调。如: ong uo
|
||||
FinalsTone = 6 // 韵母风格1,带声调,声调在韵母第一个字母上。如: ōng uó
|
||||
FinalsTone2 = 7 // 韵母风格2,带声调,声调在各个韵母之后,用数字 [1-4] 进行表示。如: o1ng uo2
|
||||
FinalsTone3 = 9 // 韵母风格3,带声调,声调在各个拼音之后,用数字 [1-4] 进行表示。如: ong1 uo2
|
||||
)
|
||||
|
||||
// 拼音风格(兼容之前的版本)
|
||||
const (
|
||||
NORMAL = Normal
|
||||
TONE = Tone
|
||||
TONE2 = Tone2
|
||||
INITIALS = Initials
|
||||
FIRST_LETTER = FirstLetter
|
||||
FINALS = Finals
|
||||
FINALS_TONE = FinalsTone
|
||||
FINALS_TONE2 = FinalsTone2
|
||||
)
|
||||
|
||||
// 声母表
|
||||
var initialArray = strings.Split(
|
||||
"b,p,m,f,d,t,n,l,g,k,h,j,q,x,r,zh,ch,sh,z,c,s",
|
||||
",",
|
||||
)
|
||||
|
||||
// 所有带声调的字符
|
||||
var rePhoneticSymbolSource = func(m map[string]string) string {
|
||||
s := ""
|
||||
for k := range m {
|
||||
s = s + k
|
||||
}
|
||||
return s
|
||||
}(phoneticSymbol)
|
||||
|
||||
// 匹配带声调字符的正则表达式
|
||||
var rePhoneticSymbol = regexp.MustCompile("[" + rePhoneticSymbolSource + "]")
|
||||
|
||||
// 匹配使用数字标识声调的字符的正则表达式
|
||||
var reTone2 = regexp.MustCompile("([aeoiuvnm])([1-4])$")
|
||||
|
||||
// 匹配 Tone2 中标识韵母声调的正则表达式
|
||||
var reTone3 = regexp.MustCompile("^([a-z]+)([1-4])([a-z]*)$")
|
||||
|
||||
// Args 配置信息
|
||||
type Args struct {
|
||||
Style int // 拼音风格(默认: Normal)
|
||||
Heteronym bool // 是否启用多音字模式(默认:禁用)
|
||||
Separator string // Slug 中使用的分隔符(默认:-)
|
||||
|
||||
// 处理没有拼音的字符(默认忽略没有拼音的字符)
|
||||
// 函数返回的 slice 的长度为0 则表示忽略这个字符
|
||||
Fallback func(r rune, a Args) []string
|
||||
}
|
||||
|
||||
// Style 默认配置:风格
|
||||
var Style = Normal
|
||||
|
||||
// Heteronym 默认配置:是否启用多音字模式
|
||||
var Heteronym = false
|
||||
|
||||
// Separator 默认配置: `Slug` 中 Join 所用的分隔符
|
||||
var Separator = "-"
|
||||
|
||||
// Fallback 默认配置: 如何处理没有拼音的字符(忽略这个字符)
|
||||
var Fallback = func(r rune, a Args) []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
var finalExceptionsMap = map[string]string{
|
||||
"ū": "ǖ",
|
||||
"ú": "ǘ",
|
||||
"ǔ": "ǚ",
|
||||
"ù": "ǜ",
|
||||
}
|
||||
var reFinalExceptions = regexp.MustCompile("^(j|q|x)(ū|ú|ǔ|ù)$")
|
||||
var reFinal2Exceptions = regexp.MustCompile("^(j|q|x)u(\\d?)$")
|
||||
|
||||
// NewArgs 返回包含默认配置的 `Args`
|
||||
func NewArgs() Args {
|
||||
return Args{Style, Heteronym, Separator, Fallback}
|
||||
}
|
||||
|
||||
// 获取单个拼音中的声母
|
||||
func initial(p string) string {
|
||||
s := ""
|
||||
for _, v := range initialArray {
|
||||
if strings.HasPrefix(p, v) {
|
||||
s = v
|
||||
break
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 获取单个拼音中的韵母
|
||||
func final(p string) string {
|
||||
n := initial(p)
|
||||
if n == "" {
|
||||
return handleYW(p)
|
||||
}
|
||||
|
||||
// 特例 j/q/x
|
||||
matches := reFinalExceptions.FindStringSubmatch(p)
|
||||
// jū -> jǖ
|
||||
if len(matches) == 3 && matches[1] != "" && matches[2] != "" {
|
||||
v, _ := finalExceptionsMap[matches[2]]
|
||||
return v
|
||||
}
|
||||
// ju -> jv, ju1 -> jv1
|
||||
p = reFinal2Exceptions.ReplaceAllString(p, "${1}v$2")
|
||||
return strings.Join(strings.SplitN(p, n, 2), "")
|
||||
}
|
||||
|
||||
// 处理 y, w
|
||||
func handleYW(p string) string {
|
||||
// 特例 y/w
|
||||
if strings.HasPrefix(p, "yu") {
|
||||
p = "v" + p[2:] // yu -> v
|
||||
} else if strings.HasPrefix(p, "yi") {
|
||||
p = p[1:] // yi -> i
|
||||
} else if strings.HasPrefix(p, "y") {
|
||||
p = "i" + p[1:] // y -> i
|
||||
} else if strings.HasPrefix(p, "wu") {
|
||||
p = p[1:] // wu -> u
|
||||
} else if strings.HasPrefix(p, "w") {
|
||||
p = "u" + p[1:] // w -> u
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func toFixed(p string, a Args) string {
|
||||
if a.Style == Initials {
|
||||
return initial(p)
|
||||
}
|
||||
origP := p
|
||||
|
||||
// 替换拼音中的带声调字符
|
||||
py := rePhoneticSymbol.ReplaceAllStringFunc(p, func(m string) string {
|
||||
symbol, _ := phoneticSymbol[m]
|
||||
switch a.Style {
|
||||
// 不包含声调
|
||||
case Normal, FirstLetter, Finals:
|
||||
// 去掉声调: a1 -> a
|
||||
m = reTone2.ReplaceAllString(symbol, "$1")
|
||||
case Tone2, FinalsTone2, Tone3, FinalsTone3:
|
||||
// 返回使用数字标识声调的字符
|
||||
m = symbol
|
||||
default:
|
||||
// 声调在头上
|
||||
}
|
||||
return m
|
||||
})
|
||||
|
||||
switch a.Style {
|
||||
// 将声调移动到最后
|
||||
case Tone3, FinalsTone3:
|
||||
py = reTone3.ReplaceAllString(py, "$1$3$2")
|
||||
}
|
||||
switch a.Style {
|
||||
// 首字母
|
||||
case FirstLetter:
|
||||
py = py[:1]
|
||||
// 韵母
|
||||
case Finals, FinalsTone, FinalsTone2, FinalsTone3:
|
||||
// 转换为 []rune unicode 编码用于获取第一个拼音字符
|
||||
// 因为 string 是 utf-8 编码不方便获取第一个拼音字符
|
||||
rs := []rune(origP)
|
||||
switch string(rs[0]) {
|
||||
// 因为鼻音没有声母所以不需要去掉声母部分
|
||||
case "ḿ", "ń", "ň", "ǹ":
|
||||
default:
|
||||
py = final(py)
|
||||
}
|
||||
}
|
||||
return py
|
||||
}
|
||||
|
||||
func applyStyle(p []string, a Args) []string {
|
||||
newP := []string{}
|
||||
for _, v := range p {
|
||||
newP = append(newP, toFixed(v, a))
|
||||
}
|
||||
return newP
|
||||
}
|
||||
|
||||
// SinglePinyin 把单个 `rune` 类型的汉字转换为拼音.
|
||||
func SinglePinyin(r rune, a Args) []string {
|
||||
if a.Fallback == nil {
|
||||
a.Fallback = Fallback
|
||||
}
|
||||
value, ok := PinyinDict[int(r)]
|
||||
pys := []string{}
|
||||
if ok {
|
||||
pys = strings.Split(value, ",")
|
||||
} else {
|
||||
pys = a.Fallback(r, a)
|
||||
}
|
||||
if len(pys) > 0 {
|
||||
if !a.Heteronym {
|
||||
pys = pys[:1]
|
||||
}
|
||||
return applyStyle(pys, a)
|
||||
}
|
||||
return pys
|
||||
}
|
||||
|
||||
// Pinyin 汉字转拼音,支持多音字模式.
|
||||
func Pinyin(s string, a Args) [][]string {
|
||||
pys := [][]string{}
|
||||
for _, r := range s {
|
||||
py := SinglePinyin(r, a)
|
||||
if len(py) > 0 {
|
||||
pys = append(pys, py)
|
||||
}
|
||||
}
|
||||
return pys
|
||||
}
|
||||
|
||||
// LazyPinyin 汉字转拼音,与 `Pinyin` 的区别是:
|
||||
// 返回值类型不同,并且不支持多音字模式,每个汉字只取第一个音.
|
||||
func LazyPinyin(s string, a Args) []string {
|
||||
a.Heteronym = false
|
||||
pys := []string{}
|
||||
for _, v := range Pinyin(s, a) {
|
||||
pys = append(pys, v[0])
|
||||
}
|
||||
return pys
|
||||
}
|
||||
|
||||
// Slug join `LazyPinyin` 的返回值.
|
||||
// 建议改用 https://github.com/mozillazg/go-slugify
|
||||
func Slug(s string, a Args) string {
|
||||
separator := a.Separator
|
||||
return strings.Join(LazyPinyin(s, a), separator)
|
||||
}
|
||||
|
||||
// Convert 跟 Pinyin 的唯一区别就是 a 参数可以是 nil
|
||||
func Convert(s string, a *Args) [][]string {
|
||||
if a == nil {
|
||||
args := NewArgs()
|
||||
a = &args
|
||||
}
|
||||
return Pinyin(s, *a)
|
||||
}
|
||||
|
||||
// LazyConvert 跟 LazyPinyin 的唯一区别就是 a 参数可以是 nil
|
||||
func LazyConvert(s string, a *Args) []string {
|
||||
if a == nil {
|
||||
args := NewArgs()
|
||||
a = &args
|
||||
}
|
||||
return LazyPinyin(s, *a)
|
||||
}
|
||||
+41457
File diff suppressed because it is too large
Load Diff
+20
-46
@@ -1,51 +1,25 @@
|
||||
language: go
|
||||
go_import_path: github.com/sirupsen/logrus
|
||||
git:
|
||||
depth: 1
|
||||
env:
|
||||
- GOMAXPROCS=4 GORACE=halt_on_error=1
|
||||
- GO111MODULE=on
|
||||
- GO111MODULE=off
|
||||
go: [ 1.11.x, 1.12.x ]
|
||||
os: [ linux, osx ]
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.10.x
|
||||
install:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go get golang.org/x/crypto/ssh/terminal
|
||||
- go get golang.org/x/sys/unix
|
||||
- go get golang.org/x/sys/windows
|
||||
script:
|
||||
- go test -race -v ./...
|
||||
- go: 1.11.x
|
||||
env: GO111MODULE=on
|
||||
install:
|
||||
- go mod download
|
||||
script:
|
||||
- go test -race -v ./...
|
||||
- go: 1.11.x
|
||||
exclude:
|
||||
- go: 1.12.x
|
||||
env: GO111MODULE=off
|
||||
install:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go get golang.org/x/crypto/ssh/terminal
|
||||
- go get golang.org/x/sys/unix
|
||||
- go get golang.org/x/sys/windows
|
||||
script:
|
||||
- go test -race -v ./...
|
||||
- go: 1.10.x
|
||||
install:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go get golang.org/x/crypto/ssh/terminal
|
||||
- go get golang.org/x/sys/unix
|
||||
- go get golang.org/x/sys/windows
|
||||
script:
|
||||
- go test -race -v -tags appengine ./...
|
||||
- go: 1.11.x
|
||||
env: GO111MODULE=on
|
||||
install:
|
||||
- go mod download
|
||||
script:
|
||||
- go test -race -v -tags appengine ./...
|
||||
- go: 1.11.x
|
||||
env: GO111MODULE=off
|
||||
install:
|
||||
- go get github.com/stretchr/testify/assert
|
||||
- go get golang.org/x/crypto/ssh/terminal
|
||||
- go get golang.org/x/sys/unix
|
||||
- go get golang.org/x/sys/windows
|
||||
script:
|
||||
- go test -race -v -tags appengine ./...
|
||||
os: osx
|
||||
install:
|
||||
- ./travis/install.sh
|
||||
- if [[ "$GO111MODULE" == "on" ]]; then go mod download; fi
|
||||
- if [[ "$GO111MODULE" == "off" ]]; then go get github.com/stretchr/testify/assert golang.org/x/sys/unix github.com/konsorten/go-windows-terminal-sequences; fi
|
||||
script:
|
||||
- ./travis/cross_build.sh
|
||||
- export GOMAXPROCS=4
|
||||
- export GORACE=halt_on_error=1
|
||||
- go test -race -v ./...
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then go test -race -v -tags appengine ./... ; fi
|
||||
|
||||
+35
@@ -1,3 +1,38 @@
|
||||
# 1.4.2
|
||||
* Fixes build break for plan9, nacl, solaris
|
||||
# 1.4.1
|
||||
This new release introduces:
|
||||
* Enhance TextFormatter to not print caller information when they are empty (#944)
|
||||
* Remove dependency on golang.org/x/crypto (#932, #943)
|
||||
|
||||
Fixes:
|
||||
* Fix Entry.WithContext method to return a copy of the initial entry (#941)
|
||||
|
||||
# 1.4.0
|
||||
This new release introduces:
|
||||
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
|
||||
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter (#909, #911)
|
||||
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
|
||||
|
||||
Fixes:
|
||||
* Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893).
|
||||
* Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903)
|
||||
* Fix infinite recursion on unknown `Level.String()` (#907)
|
||||
* Fix race condition in `getCaller` (#916).
|
||||
|
||||
|
||||
# 1.3.0
|
||||
This new release introduces:
|
||||
* Log, Logf, Logln functions for Logger and Entry that take a Level
|
||||
|
||||
Fixes:
|
||||
* Building prometheus node_exporter on AIX (#840)
|
||||
* Race condition in TextFormatter (#468)
|
||||
* Travis CI import path (#868)
|
||||
* Remove coloured output on Windows (#862)
|
||||
* Pointer to func as field in JSONFormatter (#870)
|
||||
* Properly marshal Levels (#873)
|
||||
|
||||
# 1.2.0
|
||||
This new release introduces:
|
||||
* A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued
|
||||
|
||||
+2
@@ -361,9 +361,11 @@ The built-in logging formatters are:
|
||||
Third party logging formatters:
|
||||
|
||||
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
|
||||
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
|
||||
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
||||
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
||||
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
||||
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
|
||||
|
||||
You can define your formatter by implementing the `Formatter` interface,
|
||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||
|
||||
+15
-3
@@ -51,9 +51,9 @@ func Exit(code int) {
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||
// made.
|
||||
// RegisterExitHandler appends a Logrus Exit handler to the list of handlers,
|
||||
// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
|
||||
// any Fatal log entry is made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
@@ -62,3 +62,15 @@ func Exit(code int) {
|
||||
func RegisterExitHandler(handler func()) {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
||||
|
||||
// DeferExitHandler prepends a Logrus Exit handler to the list of handlers,
|
||||
// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
|
||||
// any Fatal log entry is made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
// closing database connections, or sending a alert that the application is
|
||||
// closing.
|
||||
func DeferExitHandler(handler func()) {
|
||||
handlers = append([]func(){handler}, handlers...)
|
||||
}
|
||||
|
||||
+78
-79
@@ -2,6 +2,7 @@ package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
@@ -69,6 +70,9 @@ type Entry struct {
|
||||
// When formatter is called in entry.log(), a Buffer may be set to entry
|
||||
Buffer *bytes.Buffer
|
||||
|
||||
// Contains the context set by the user. Useful for hook processing etc.
|
||||
Context context.Context
|
||||
|
||||
// err may contain a field formatting error
|
||||
err string
|
||||
}
|
||||
@@ -97,6 +101,11 @@ func (entry *Entry) WithError(err error) *Entry {
|
||||
return entry.WithField(ErrorKey, err)
|
||||
}
|
||||
|
||||
// Add a context to the Entry.
|
||||
func (entry *Entry) WithContext(ctx context.Context) *Entry {
|
||||
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: entry.Time, err: entry.err, Context: ctx}
|
||||
}
|
||||
|
||||
// Add a single field to the Entry.
|
||||
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
||||
return entry.WithFields(Fields{key: value})
|
||||
@@ -108,23 +117,34 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
var field_err string
|
||||
fieldErr := entry.err
|
||||
for k, v := range fields {
|
||||
if t := reflect.TypeOf(v); t != nil && t.Kind() == reflect.Func {
|
||||
field_err = fmt.Sprintf("can not add field %q", k)
|
||||
if entry.err != "" {
|
||||
field_err = entry.err + ", " + field_err
|
||||
isErrField := false
|
||||
if t := reflect.TypeOf(v); t != nil {
|
||||
switch t.Kind() {
|
||||
case reflect.Func:
|
||||
isErrField = true
|
||||
case reflect.Ptr:
|
||||
isErrField = t.Elem().Kind() == reflect.Func
|
||||
}
|
||||
}
|
||||
if isErrField {
|
||||
tmp := fmt.Sprintf("can not add field %q", k)
|
||||
if fieldErr != "" {
|
||||
fieldErr = entry.err + ", " + tmp
|
||||
} else {
|
||||
fieldErr = tmp
|
||||
}
|
||||
} else {
|
||||
data[k] = v
|
||||
}
|
||||
}
|
||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: field_err}
|
||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
|
||||
}
|
||||
|
||||
// Overrides the time of the Entry.
|
||||
func (entry *Entry) WithTime(t time.Time) *Entry {
|
||||
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t}
|
||||
return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t, err: entry.err, Context: entry.Context}
|
||||
}
|
||||
|
||||
// getPackageName reduces a fully qualified function name to the package name
|
||||
@@ -145,20 +165,23 @@ func getPackageName(f string) string {
|
||||
|
||||
// getCaller retrieves the name of the first non-logrus calling function
|
||||
func getCaller() *runtime.Frame {
|
||||
|
||||
// cache this package's fully-qualified name
|
||||
callerInitOnce.Do(func() {
|
||||
pcs := make([]uintptr, 2)
|
||||
_ = runtime.Callers(0, pcs)
|
||||
logrusPackage = getPackageName(runtime.FuncForPC(pcs[1]).Name())
|
||||
|
||||
// now that we have the cache, we can skip a minimum count of known-logrus functions
|
||||
// XXX this is dubious, the number of frames may vary
|
||||
minimumCallerDepth = knownLogrusFrames
|
||||
})
|
||||
|
||||
// Restrict the lookback frames to avoid runaway lookups
|
||||
pcs := make([]uintptr, maximumCallerDepth)
|
||||
depth := runtime.Callers(minimumCallerDepth, pcs)
|
||||
frames := runtime.CallersFrames(pcs[:depth])
|
||||
|
||||
// cache this package's fully-qualified name
|
||||
callerInitOnce.Do(func() {
|
||||
logrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name())
|
||||
|
||||
// now that we have the cache, we can skip a minimum count of known-logrus functions
|
||||
// XXX this is dubious, the number of frames may vary store an entry in a logger interface
|
||||
minimumCallerDepth = knownLogrusFrames
|
||||
})
|
||||
|
||||
for f, again := frames.Next(); again; f, again = frames.Next() {
|
||||
pkg := getPackageName(f.Function)
|
||||
|
||||
@@ -240,16 +263,18 @@ func (entry *Entry) write() {
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Trace(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(TraceLevel) {
|
||||
entry.log(TraceLevel, fmt.Sprint(args...))
|
||||
func (entry *Entry) Log(level Level, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(level) {
|
||||
entry.log(level, fmt.Sprint(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Trace(args ...interface{}) {
|
||||
entry.Log(TraceLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Debug(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(DebugLevel) {
|
||||
entry.log(DebugLevel, fmt.Sprint(args...))
|
||||
}
|
||||
entry.Log(DebugLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Print(args ...interface{}) {
|
||||
@@ -257,15 +282,11 @@ func (entry *Entry) Print(args ...interface{}) {
|
||||
}
|
||||
|
||||
func (entry *Entry) Info(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(InfoLevel) {
|
||||
entry.log(InfoLevel, fmt.Sprint(args...))
|
||||
}
|
||||
entry.Log(InfoLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warn(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(WarnLevel) {
|
||||
entry.log(WarnLevel, fmt.Sprint(args...))
|
||||
}
|
||||
entry.Log(WarnLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warning(args ...interface{}) {
|
||||
@@ -273,43 +294,37 @@ func (entry *Entry) Warning(args ...interface{}) {
|
||||
}
|
||||
|
||||
func (entry *Entry) Error(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(ErrorLevel) {
|
||||
entry.log(ErrorLevel, fmt.Sprint(args...))
|
||||
}
|
||||
entry.Log(ErrorLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Fatal(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(FatalLevel) {
|
||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||
}
|
||||
entry.Log(FatalLevel, args...)
|
||||
entry.Logger.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(PanicLevel) {
|
||||
entry.log(PanicLevel, fmt.Sprint(args...))
|
||||
}
|
||||
entry.Log(PanicLevel, args...)
|
||||
panic(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
// Entry Printf family functions
|
||||
|
||||
func (entry *Entry) Tracef(format string, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(TraceLevel) {
|
||||
entry.Trace(fmt.Sprintf(format, args...))
|
||||
func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(level) {
|
||||
entry.Log(level, fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Tracef(format string, args ...interface{}) {
|
||||
entry.Logf(TraceLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(DebugLevel) {
|
||||
entry.Debug(fmt.Sprintf(format, args...))
|
||||
}
|
||||
entry.Logf(DebugLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Infof(format string, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(InfoLevel) {
|
||||
entry.Info(fmt.Sprintf(format, args...))
|
||||
}
|
||||
entry.Logf(InfoLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Printf(format string, args ...interface{}) {
|
||||
@@ -317,9 +332,7 @@ func (entry *Entry) Printf(format string, args ...interface{}) {
|
||||
}
|
||||
|
||||
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(WarnLevel) {
|
||||
entry.Warn(fmt.Sprintf(format, args...))
|
||||
}
|
||||
entry.Logf(WarnLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
||||
@@ -327,42 +340,36 @@ func (entry *Entry) Warningf(format string, args ...interface{}) {
|
||||
}
|
||||
|
||||
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(ErrorLevel) {
|
||||
entry.Error(fmt.Sprintf(format, args...))
|
||||
}
|
||||
entry.Logf(ErrorLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(FatalLevel) {
|
||||
entry.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
entry.Logf(FatalLevel, format, args...)
|
||||
entry.Logger.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(PanicLevel) {
|
||||
entry.Panic(fmt.Sprintf(format, args...))
|
||||
}
|
||||
entry.Logf(PanicLevel, format, args...)
|
||||
}
|
||||
|
||||
// Entry Println family functions
|
||||
|
||||
func (entry *Entry) Traceln(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(TraceLevel) {
|
||||
entry.Trace(entry.sprintlnn(args...))
|
||||
func (entry *Entry) Logln(level Level, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(level) {
|
||||
entry.Log(level, entry.sprintlnn(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Traceln(args ...interface{}) {
|
||||
entry.Logln(TraceLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Debugln(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(DebugLevel) {
|
||||
entry.Debug(entry.sprintlnn(args...))
|
||||
}
|
||||
entry.Logln(DebugLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Infoln(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(InfoLevel) {
|
||||
entry.Info(entry.sprintlnn(args...))
|
||||
}
|
||||
entry.Logln(InfoLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Println(args ...interface{}) {
|
||||
@@ -370,9 +377,7 @@ func (entry *Entry) Println(args ...interface{}) {
|
||||
}
|
||||
|
||||
func (entry *Entry) Warnln(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(WarnLevel) {
|
||||
entry.Warn(entry.sprintlnn(args...))
|
||||
}
|
||||
entry.Logln(WarnLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warningln(args ...interface{}) {
|
||||
@@ -380,22 +385,16 @@ func (entry *Entry) Warningln(args ...interface{}) {
|
||||
}
|
||||
|
||||
func (entry *Entry) Errorln(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(ErrorLevel) {
|
||||
entry.Error(entry.sprintlnn(args...))
|
||||
}
|
||||
entry.Logln(ErrorLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Fatalln(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(FatalLevel) {
|
||||
entry.Fatal(entry.sprintlnn(args...))
|
||||
}
|
||||
entry.Logln(FatalLevel, args...)
|
||||
entry.Logger.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(PanicLevel) {
|
||||
entry.Panic(entry.sprintlnn(args...))
|
||||
}
|
||||
entry.Logln(PanicLevel, args...)
|
||||
}
|
||||
|
||||
// Sprintlnn => Sprint no newline. This is to get the behavior of how
|
||||
|
||||
+6
@@ -1,6 +1,7 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
@@ -55,6 +56,11 @@ func WithError(err error) *Entry {
|
||||
return std.WithField(ErrorKey, err)
|
||||
}
|
||||
|
||||
// WithContext creates an entry from the standard logger and adds a context to it.
|
||||
func WithContext(ctx context.Context) *Entry {
|
||||
return std.WithContext(ctx)
|
||||
}
|
||||
|
||||
// WithField creates an entry from the standard logger and adds a field to
|
||||
// it. If you want multiple fields, use `WithFields`.
|
||||
//
|
||||
|
||||
+1
-2
@@ -6,6 +6,5 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.1.1 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894
|
||||
)
|
||||
|
||||
+3
-2
@@ -2,6 +2,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe h1:CHRGQ8V7OlCYtwaKPJi3iA7J+YdNKdo8j7nG5IgDhjs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -9,7 +10,7 @@ github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
+19
-3
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type fieldKey string
|
||||
@@ -42,6 +43,12 @@ type JSONFormatter struct {
|
||||
// }
|
||||
FieldMap FieldMap
|
||||
|
||||
// CallerPrettyfier can be set by the user to modify the content
|
||||
// of the function and file keys in the json data when ReportCaller is
|
||||
// activated. If any of the returned value is the empty string the
|
||||
// corresponding key will be removed from json fields.
|
||||
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
||||
|
||||
// PrettyPrint will indent all json logs
|
||||
PrettyPrint bool
|
||||
}
|
||||
@@ -82,8 +89,17 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
||||
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
||||
if entry.HasCaller() {
|
||||
data[f.FieldMap.resolve(FieldKeyFunc)] = entry.Caller.Function
|
||||
data[f.FieldMap.resolve(FieldKeyFile)] = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||
funcVal := entry.Caller.Function
|
||||
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||
if f.CallerPrettyfier != nil {
|
||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
||||
}
|
||||
if funcVal != "" {
|
||||
data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
|
||||
}
|
||||
if fileVal != "" {
|
||||
data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
|
||||
}
|
||||
}
|
||||
|
||||
var b *bytes.Buffer
|
||||
@@ -98,7 +114,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
encoder.SetIndent("", " ")
|
||||
}
|
||||
if err := encoder.Encode(data); err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err)
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
|
||||
+49
-113
@@ -1,6 +1,7 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
@@ -124,6 +125,13 @@ func (logger *Logger) WithError(err error) *Entry {
|
||||
return entry.WithError(err)
|
||||
}
|
||||
|
||||
// Add a context to the log entry.
|
||||
func (logger *Logger) WithContext(ctx context.Context) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithContext(ctx)
|
||||
}
|
||||
|
||||
// Overrides the time of the log entry.
|
||||
func (logger *Logger) WithTime(t time.Time) *Entry {
|
||||
entry := logger.newEntry()
|
||||
@@ -131,28 +139,24 @@ func (logger *Logger) WithTime(t time.Time) *Entry {
|
||||
return entry.WithTime(t)
|
||||
}
|
||||
|
||||
func (logger *Logger) Tracef(format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(TraceLevel) {
|
||||
func (logger *Logger) Logf(level Level, format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(level) {
|
||||
entry := logger.newEntry()
|
||||
entry.Tracef(format, args...)
|
||||
entry.Logf(level, format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Tracef(format string, args ...interface{}) {
|
||||
logger.Logf(TraceLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(DebugLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Debugf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logf(DebugLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(InfoLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Infof(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logf(InfoLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
@@ -162,139 +166,91 @@ func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(WarnLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logf(WarnLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(WarnLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Warnf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(ErrorLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Errorf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logf(ErrorLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(FatalLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logf(FatalLevel, format, args...)
|
||||
logger.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(PanicLevel) {
|
||||
logger.Logf(PanicLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Log(level Level, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(level) {
|
||||
entry := logger.newEntry()
|
||||
entry.Panicf(format, args...)
|
||||
entry.Log(level, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Trace(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(TraceLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Trace(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Log(TraceLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debug(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(DebugLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Debug(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Log(DebugLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Info(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(InfoLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Log(InfoLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Print(args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
entry.Print(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warn(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(WarnLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Log(WarnLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warning(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(WarnLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Warn(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Error(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(ErrorLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Error(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Log(ErrorLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatal(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(FatalLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Fatal(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Log(FatalLevel, args...)
|
||||
logger.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panic(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(PanicLevel) {
|
||||
logger.Log(PanicLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Logln(level Level, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(level) {
|
||||
entry := logger.newEntry()
|
||||
entry.Panic(args...)
|
||||
entry.Logln(level, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Traceln(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(TraceLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Traceln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logln(TraceLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugln(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(DebugLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Debugln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logln(DebugLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Infoln(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(InfoLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Infoln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logln(InfoLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Println(args ...interface{}) {
|
||||
@@ -304,44 +260,24 @@ func (logger *Logger) Println(args ...interface{}) {
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnln(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(WarnLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logln(WarnLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningln(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(WarnLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Warnln(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorln(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(ErrorLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Errorln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logln(ErrorLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(FatalLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logln(FatalLevel, args...)
|
||||
logger.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicln(args ...interface{}) {
|
||||
if logger.IsLevelEnabled(PanicLevel) {
|
||||
entry := logger.newEntry()
|
||||
entry.Panicln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
logger.Logln(PanicLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Exit(code int) {
|
||||
|
||||
+25
-17
@@ -14,24 +14,11 @@ type Level uint32
|
||||
|
||||
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
|
||||
func (level Level) String() string {
|
||||
switch level {
|
||||
case TraceLevel:
|
||||
return "trace"
|
||||
case DebugLevel:
|
||||
return "debug"
|
||||
case InfoLevel:
|
||||
return "info"
|
||||
case WarnLevel:
|
||||
return "warning"
|
||||
case ErrorLevel:
|
||||
return "error"
|
||||
case FatalLevel:
|
||||
return "fatal"
|
||||
case PanicLevel:
|
||||
return "panic"
|
||||
if b, err := level.MarshalText(); err == nil {
|
||||
return string(b)
|
||||
} else {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||
@@ -69,6 +56,27 @@ func (level *Level) UnmarshalText(text []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (level Level) MarshalText() ([]byte, error) {
|
||||
switch level {
|
||||
case TraceLevel:
|
||||
return []byte("trace"), nil
|
||||
case DebugLevel:
|
||||
return []byte("debug"), nil
|
||||
case InfoLevel:
|
||||
return []byte("info"), nil
|
||||
case WarnLevel:
|
||||
return []byte("warning"), nil
|
||||
case ErrorLevel:
|
||||
return []byte("error"), nil
|
||||
case FatalLevel:
|
||||
return []byte("fatal"), nil
|
||||
case PanicLevel:
|
||||
return []byte("panic"), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("not a valid logrus level %d", level)
|
||||
}
|
||||
|
||||
// A constant exposing all logging levels
|
||||
var AllLevels = []Level{
|
||||
PanicLevel,
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package logrus
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TIOCGETA
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build js
|
||||
// +build js nacl plan9
|
||||
|
||||
package logrus
|
||||
|
||||
+2
-4
@@ -1,18 +1,16 @@
|
||||
// +build !appengine,!js,!windows
|
||||
// +build !appengine,!js,!windows,!nacl,!plan9
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
func checkIfTerminal(w io.Writer) bool {
|
||||
switch v := w.(type) {
|
||||
case *os.File:
|
||||
return terminal.IsTerminal(int(v.Fd()))
|
||||
return isTerminal(int(v.Fd()))
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func isTerminal(fd int) bool {
|
||||
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// +build linux aix
|
||||
|
||||
package logrus
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TCGETS
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
+16
-2
@@ -6,15 +6,29 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
sequences "github.com/konsorten/go-windows-terminal-sequences"
|
||||
)
|
||||
|
||||
func initTerminal(w io.Writer) {
|
||||
switch v := w.(type) {
|
||||
case *os.File:
|
||||
sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true)
|
||||
}
|
||||
}
|
||||
|
||||
func checkIfTerminal(w io.Writer) bool {
|
||||
var ret bool
|
||||
switch v := w.(type) {
|
||||
case *os.File:
|
||||
var mode uint32
|
||||
err := syscall.GetConsoleMode(syscall.Handle(v.Fd()), &mode)
|
||||
return err == nil
|
||||
ret = (err == nil)
|
||||
default:
|
||||
return false
|
||||
ret = false
|
||||
}
|
||||
if ret {
|
||||
initTerminal(w)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package logrus
|
||||
|
||||
import "io"
|
||||
|
||||
func initTerminal(w io.Writer) {
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
// +build !appengine,!js,windows
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
sequences "github.com/konsorten/go-windows-terminal-sequences"
|
||||
)
|
||||
|
||||
func initTerminal(w io.Writer) {
|
||||
switch v := w.(type) {
|
||||
case *os.File:
|
||||
sequences.EnableVirtualTerminalProcessing(syscall.Handle(v.Fd()), true)
|
||||
}
|
||||
}
|
||||
+57
-31
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -11,18 +12,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
nocolor = 0
|
||||
red = 31
|
||||
green = 32
|
||||
yellow = 33
|
||||
blue = 36
|
||||
gray = 37
|
||||
red = 31
|
||||
yellow = 33
|
||||
blue = 36
|
||||
gray = 37
|
||||
)
|
||||
|
||||
var (
|
||||
baseTimestamp time.Time
|
||||
emptyFieldMap FieldMap
|
||||
)
|
||||
var baseTimestamp time.Time
|
||||
|
||||
func init() {
|
||||
baseTimestamp = time.Now()
|
||||
@@ -76,21 +72,23 @@ type TextFormatter struct {
|
||||
// FieldKeyMsg: "@message"}}
|
||||
FieldMap FieldMap
|
||||
|
||||
// CallerPrettyfier can be set by the user to modify the content
|
||||
// of the function and file keys in the data when ReportCaller is
|
||||
// activated. If any of the returned value is the empty string the
|
||||
// corresponding key will be removed from fields.
|
||||
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
||||
|
||||
terminalInitOnce sync.Once
|
||||
}
|
||||
|
||||
func (f *TextFormatter) init(entry *Entry) {
|
||||
if entry.Logger != nil {
|
||||
f.isTerminal = checkIfTerminal(entry.Logger.Out)
|
||||
|
||||
if f.isTerminal {
|
||||
initTerminal(entry.Logger.Out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TextFormatter) isColored() bool {
|
||||
isColored := f.ForceColors || f.isTerminal
|
||||
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
|
||||
|
||||
if f.EnvironmentOverrideColors {
|
||||
if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" {
|
||||
@@ -107,14 +105,19 @@ func (f *TextFormatter) isColored() bool {
|
||||
|
||||
// Format renders a single log entry
|
||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
prefixFieldClashes(entry.Data, f.FieldMap, entry.HasCaller())
|
||||
|
||||
keys := make([]string, 0, len(entry.Data))
|
||||
for k := range entry.Data {
|
||||
data := make(Fields)
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
||||
keys := make([]string, 0, len(data))
|
||||
for k := range data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
fixedKeys := make([]string, 0, 4+len(entry.Data))
|
||||
var funcVal, fileVal string
|
||||
|
||||
fixedKeys := make([]string, 0, 4+len(data))
|
||||
if !f.DisableTimestamp {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
|
||||
}
|
||||
@@ -126,8 +129,19 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
|
||||
}
|
||||
if entry.HasCaller() {
|
||||
fixedKeys = append(fixedKeys,
|
||||
f.FieldMap.resolve(FieldKeyFunc), f.FieldMap.resolve(FieldKeyFile))
|
||||
if f.CallerPrettyfier != nil {
|
||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
||||
} else {
|
||||
funcVal = entry.Caller.Function
|
||||
fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||
}
|
||||
|
||||
if funcVal != "" {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))
|
||||
}
|
||||
if fileVal != "" {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))
|
||||
}
|
||||
}
|
||||
|
||||
if !f.DisableSorting {
|
||||
@@ -160,8 +174,9 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
timestampFormat = defaultTimestampFormat
|
||||
}
|
||||
if f.isColored() {
|
||||
f.printColored(b, entry, keys, timestampFormat)
|
||||
f.printColored(b, entry, keys, data, timestampFormat)
|
||||
} else {
|
||||
|
||||
for _, key := range fixedKeys {
|
||||
var value interface{}
|
||||
switch {
|
||||
@@ -174,11 +189,11 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
case key == f.FieldMap.resolve(FieldKeyLogrusError):
|
||||
value = entry.err
|
||||
case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():
|
||||
value = entry.Caller.Function
|
||||
value = funcVal
|
||||
case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
|
||||
value = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||
value = fileVal
|
||||
default:
|
||||
value = entry.Data[key]
|
||||
value = data[key]
|
||||
}
|
||||
f.appendKeyValue(b, key, value)
|
||||
}
|
||||
@@ -188,7 +203,7 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
|
||||
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
|
||||
var levelColor int
|
||||
switch entry.Level {
|
||||
case DebugLevel, TraceLevel:
|
||||
@@ -211,10 +226,21 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||
entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
||||
|
||||
caller := ""
|
||||
|
||||
if entry.HasCaller() {
|
||||
caller = fmt.Sprintf("%s:%d %s()",
|
||||
entry.Caller.File, entry.Caller.Line, entry.Caller.Function)
|
||||
funcVal := fmt.Sprintf("%s()", entry.Caller.Function)
|
||||
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||
|
||||
if f.CallerPrettyfier != nil {
|
||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
||||
}
|
||||
|
||||
if fileVal == "" {
|
||||
caller = funcVal
|
||||
} else if funcVal == "" {
|
||||
caller = fileVal
|
||||
} else {
|
||||
caller = fileVal + " " + funcVal
|
||||
}
|
||||
}
|
||||
|
||||
if f.DisableTimestamp {
|
||||
@@ -225,7 +251,7 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
|
||||
}
|
||||
for _, k := range keys {
|
||||
v := entry.Data[k]
|
||||
v := data[k]
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||
f.appendValue(b, v)
|
||||
}
|
||||
|
||||
+45
-45
@@ -121,18 +121,18 @@ TEXT ·ladderstep(SB),0,$296-8
|
||||
ADDQ AX,R12
|
||||
ADCQ DX,R13
|
||||
MOVQ $REDMASK51,DX
|
||||
SHLQ $13,CX:SI
|
||||
SHLQ $13,SI,CX
|
||||
ANDQ DX,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ DX,R8
|
||||
ADDQ CX,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ DX,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ DX,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ DX,R14
|
||||
ADDQ R13,R14
|
||||
IMUL3Q $19,R15,CX
|
||||
@@ -236,18 +236,18 @@ TEXT ·ladderstep(SB),0,$296-8
|
||||
ADDQ AX,R12
|
||||
ADCQ DX,R13
|
||||
MOVQ $REDMASK51,DX
|
||||
SHLQ $13,CX:SI
|
||||
SHLQ $13,SI,CX
|
||||
ANDQ DX,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ DX,R8
|
||||
ADDQ CX,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ DX,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ DX,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ DX,R14
|
||||
ADDQ R13,R14
|
||||
IMUL3Q $19,R15,CX
|
||||
@@ -441,18 +441,18 @@ TEXT ·ladderstep(SB),0,$296-8
|
||||
ADDQ AX,R12
|
||||
ADCQ DX,R13
|
||||
MOVQ $REDMASK51,DX
|
||||
SHLQ $13,CX:SI
|
||||
SHLQ $13,SI,CX
|
||||
ANDQ DX,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ DX,R8
|
||||
ADDQ CX,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ DX,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ DX,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ DX,R14
|
||||
ADDQ R13,R14
|
||||
IMUL3Q $19,R15,CX
|
||||
@@ -591,18 +591,18 @@ TEXT ·ladderstep(SB),0,$296-8
|
||||
ADDQ AX,R12
|
||||
ADCQ DX,R13
|
||||
MOVQ $REDMASK51,DX
|
||||
SHLQ $13,CX:SI
|
||||
SHLQ $13,SI,CX
|
||||
ANDQ DX,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ DX,R8
|
||||
ADDQ CX,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ DX,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ DX,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ DX,R14
|
||||
ADDQ R13,R14
|
||||
IMUL3Q $19,R15,CX
|
||||
@@ -731,18 +731,18 @@ TEXT ·ladderstep(SB),0,$296-8
|
||||
ADDQ AX,R12
|
||||
ADCQ DX,R13
|
||||
MOVQ $REDMASK51,DX
|
||||
SHLQ $13,CX:SI
|
||||
SHLQ $13,SI,CX
|
||||
ANDQ DX,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ DX,R8
|
||||
ADDQ CX,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ DX,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ DX,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ DX,R14
|
||||
ADDQ R13,R14
|
||||
IMUL3Q $19,R15,CX
|
||||
@@ -846,18 +846,18 @@ TEXT ·ladderstep(SB),0,$296-8
|
||||
ADDQ AX,R12
|
||||
ADCQ DX,R13
|
||||
MOVQ $REDMASK51,DX
|
||||
SHLQ $13,CX:SI
|
||||
SHLQ $13,SI,CX
|
||||
ANDQ DX,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ DX,R8
|
||||
ADDQ CX,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ DX,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ DX,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ DX,R14
|
||||
ADDQ R13,R14
|
||||
IMUL3Q $19,R15,CX
|
||||
@@ -996,18 +996,18 @@ TEXT ·ladderstep(SB),0,$296-8
|
||||
ADDQ AX,R12
|
||||
ADCQ DX,R13
|
||||
MOVQ $REDMASK51,DX
|
||||
SHLQ $13,CX:SI
|
||||
SHLQ $13,SI,CX
|
||||
ANDQ DX,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ DX,R8
|
||||
ADDQ CX,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ DX,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ DX,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ DX,R14
|
||||
ADDQ R13,R14
|
||||
IMUL3Q $19,R15,CX
|
||||
@@ -1146,18 +1146,18 @@ TEXT ·ladderstep(SB),0,$296-8
|
||||
ADDQ AX,R12
|
||||
ADCQ DX,R13
|
||||
MOVQ $REDMASK51,DX
|
||||
SHLQ $13,CX:SI
|
||||
SHLQ $13,SI,CX
|
||||
ANDQ DX,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ DX,R8
|
||||
ADDQ CX,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ DX,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ DX,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ DX,R14
|
||||
ADDQ R13,R14
|
||||
IMUL3Q $19,R15,CX
|
||||
@@ -1332,18 +1332,18 @@ TEXT ·ladderstep(SB),0,$296-8
|
||||
ADDQ AX,R12
|
||||
ADCQ DX,R13
|
||||
MOVQ $REDMASK51,DX
|
||||
SHLQ $13,CX:SI
|
||||
SHLQ $13,SI,CX
|
||||
ANDQ DX,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ DX,R8
|
||||
ADDQ CX,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ DX,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ DX,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ DX,R14
|
||||
ADDQ R13,R14
|
||||
IMUL3Q $19,R15,CX
|
||||
|
||||
+5
-5
@@ -124,18 +124,18 @@ TEXT ·mul(SB),0,$16-24
|
||||
ADDQ AX,R14
|
||||
ADCQ DX,R15
|
||||
MOVQ $REDMASK51,SI
|
||||
SHLQ $13,R9:R8
|
||||
SHLQ $13,R8,R9
|
||||
ANDQ SI,R8
|
||||
SHLQ $13,R11:R10
|
||||
SHLQ $13,R10,R11
|
||||
ANDQ SI,R10
|
||||
ADDQ R9,R10
|
||||
SHLQ $13,R13:R12
|
||||
SHLQ $13,R12,R13
|
||||
ANDQ SI,R12
|
||||
ADDQ R11,R12
|
||||
SHLQ $13,R15:R14
|
||||
SHLQ $13,R14,R15
|
||||
ANDQ SI,R14
|
||||
ADDQ R13,R14
|
||||
SHLQ $13,BP:BX
|
||||
SHLQ $13,BX,BP
|
||||
ANDQ SI,BX
|
||||
ADDQ R15,BX
|
||||
IMUL3Q $19,BP,DX
|
||||
|
||||
+5
-5
@@ -87,18 +87,18 @@ TEXT ·square(SB),7,$0-16
|
||||
ADDQ AX,R13
|
||||
ADCQ DX,R14
|
||||
MOVQ $REDMASK51,SI
|
||||
SHLQ $13,R8:CX
|
||||
SHLQ $13,CX,R8
|
||||
ANDQ SI,CX
|
||||
SHLQ $13,R10:R9
|
||||
SHLQ $13,R9,R10
|
||||
ANDQ SI,R9
|
||||
ADDQ R8,R9
|
||||
SHLQ $13,R12:R11
|
||||
SHLQ $13,R11,R12
|
||||
ANDQ SI,R11
|
||||
ADDQ R10,R11
|
||||
SHLQ $13,R14:R13
|
||||
SHLQ $13,R13,R14
|
||||
ANDQ SI,R13
|
||||
ADDQ R12,R13
|
||||
SHLQ $13,BX:R15
|
||||
SHLQ $13,R15,BX
|
||||
ANDQ SI,R15
|
||||
ADDQ R14,R15
|
||||
IMUL3Q $19,BX,DX
|
||||
|
||||
+5
@@ -2,6 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// In Go 1.13, the ed25519 package was promoted to the standard library as
|
||||
// crypto/ed25519, and this package became a wrapper for the standard library one.
|
||||
//
|
||||
// +build !go1.13
|
||||
|
||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
||||
// https://ed25519.cr.yp.to/.
|
||||
//
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.13
|
||||
|
||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
||||
// https://ed25519.cr.yp.to/.
|
||||
//
|
||||
// These functions are also compatible with the “Ed25519” function defined in
|
||||
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
||||
// representation includes a public key suffix to make multiple signing
|
||||
// operations with the same key more efficient. This package refers to the RFC
|
||||
// 8032 private key as the “seed”.
|
||||
//
|
||||
// Beginning with Go 1.13, the functionality of this package was moved to the
|
||||
// standard library as crypto/ed25519. This package only acts as a compatibility
|
||||
// wrapper.
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// PublicKeySize is the size, in bytes, of public keys as used in this package.
|
||||
PublicKeySize = 32
|
||||
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
|
||||
PrivateKeySize = 64
|
||||
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
|
||||
SignatureSize = 64
|
||||
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
|
||||
SeedSize = 32
|
||||
)
|
||||
|
||||
// PublicKey is the type of Ed25519 public keys.
|
||||
//
|
||||
// This type is an alias for crypto/ed25519's PublicKey type.
|
||||
// See the crypto/ed25519 package for the methods on this type.
|
||||
type PublicKey = ed25519.PublicKey
|
||||
|
||||
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
|
||||
//
|
||||
// This type is an alias for crypto/ed25519's PrivateKey type.
|
||||
// See the crypto/ed25519 package for the methods on this type.
|
||||
type PrivateKey = ed25519.PrivateKey
|
||||
|
||||
// GenerateKey generates a public/private key pair using entropy from rand.
|
||||
// If rand is nil, crypto/rand.Reader will be used.
|
||||
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
|
||||
return ed25519.GenerateKey(rand)
|
||||
}
|
||||
|
||||
// NewKeyFromSeed calculates a private key from a seed. It will panic if
|
||||
// len(seed) is not SeedSize. This function is provided for interoperability
|
||||
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
|
||||
// package.
|
||||
func NewKeyFromSeed(seed []byte) PrivateKey {
|
||||
return ed25519.NewKeyFromSeed(seed)
|
||||
}
|
||||
|
||||
// Sign signs the message with privateKey and returns a signature. It will
|
||||
// panic if len(privateKey) is not PrivateKeySize.
|
||||
func Sign(privateKey PrivateKey, message []byte) []byte {
|
||||
return ed25519.Sign(privateKey, message)
|
||||
}
|
||||
|
||||
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||
// will panic if len(publicKey) is not PublicKeySize.
|
||||
func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
return ed25519.Verify(publicKey, message, sig)
|
||||
}
|
||||
+668
@@ -0,0 +1,668 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Based on CRYPTOGAMS code with the following comment:
|
||||
// # ====================================================================
|
||||
// # Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
|
||||
// # project. The module is, however, dual licensed under OpenSSL and
|
||||
// # CRYPTOGAMS licenses depending on where you obtain it. For further
|
||||
// # details see http://www.openssl.org/~appro/cryptogams/.
|
||||
// # ====================================================================
|
||||
|
||||
// Original code can be found at the link below:
|
||||
// https://github.com/dot-asm/cryptogams/commit/a60f5b50ed908e91e5c39ca79126a4a876d5d8ff
|
||||
|
||||
// There are some differences between CRYPTOGAMS code and this one. The round
|
||||
// loop for "_int" isn't the same as the original. Some adjustments were
|
||||
// necessary because there are less vector registers available. For example, some
|
||||
// X variables (r12, r13, r14, and r15) share the same register used by the
|
||||
// counter. The original code uses ctr to name the counter. Here we use CNT
|
||||
// because golang uses CTR as the counter register name.
|
||||
|
||||
// +build ppc64le,!gccgo,!appengine
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define OUT R3
|
||||
#define INP R4
|
||||
#define LEN R5
|
||||
#define KEY R6
|
||||
#define CNT R7
|
||||
|
||||
#define TEMP R8
|
||||
|
||||
#define X0 R11
|
||||
#define X1 R12
|
||||
#define X2 R14
|
||||
#define X3 R15
|
||||
#define X4 R16
|
||||
#define X5 R17
|
||||
#define X6 R18
|
||||
#define X7 R19
|
||||
#define X8 R20
|
||||
#define X9 R21
|
||||
#define X10 R22
|
||||
#define X11 R23
|
||||
#define X12 R24
|
||||
#define X13 R25
|
||||
#define X14 R26
|
||||
#define X15 R27
|
||||
|
||||
#define CON0 X0
|
||||
#define CON1 X1
|
||||
#define CON2 X2
|
||||
#define CON3 X3
|
||||
|
||||
#define KEY0 X4
|
||||
#define KEY1 X5
|
||||
#define KEY2 X6
|
||||
#define KEY3 X7
|
||||
#define KEY4 X8
|
||||
#define KEY5 X9
|
||||
#define KEY6 X10
|
||||
#define KEY7 X11
|
||||
|
||||
#define CNT0 X12
|
||||
#define CNT1 X13
|
||||
#define CNT2 X14
|
||||
#define CNT3 X15
|
||||
|
||||
#define TMP0 R9
|
||||
#define TMP1 R10
|
||||
#define TMP2 R28
|
||||
#define TMP3 R29
|
||||
|
||||
#define CONSTS R8
|
||||
|
||||
#define A0 V0
|
||||
#define B0 V1
|
||||
#define C0 V2
|
||||
#define D0 V3
|
||||
#define A1 V4
|
||||
#define B1 V5
|
||||
#define C1 V6
|
||||
#define D1 V7
|
||||
#define A2 V8
|
||||
#define B2 V9
|
||||
#define C2 V10
|
||||
#define D2 V11
|
||||
#define T0 V12
|
||||
#define T1 V13
|
||||
#define T2 V14
|
||||
|
||||
#define K0 V15
|
||||
#define K1 V16
|
||||
#define K2 V17
|
||||
#define K3 V18
|
||||
#define K4 V19
|
||||
#define K5 V20
|
||||
|
||||
#define FOUR V21
|
||||
#define SIXTEEN V22
|
||||
#define TWENTY4 V23
|
||||
#define TWENTY V24
|
||||
#define TWELVE V25
|
||||
#define TWENTY5 V26
|
||||
#define SEVEN V27
|
||||
|
||||
#define INPPERM V28
|
||||
#define OUTPERM V29
|
||||
#define OUTMASK V30
|
||||
|
||||
#define DD0 V31
|
||||
#define DD1 SEVEN
|
||||
#define DD2 T0
|
||||
#define DD3 T1
|
||||
#define DD4 T2
|
||||
|
||||
DATA ·consts+0x00(SB)/8, $0x3320646e61707865
|
||||
DATA ·consts+0x08(SB)/8, $0x6b20657479622d32
|
||||
DATA ·consts+0x10(SB)/8, $0x0000000000000001
|
||||
DATA ·consts+0x18(SB)/8, $0x0000000000000000
|
||||
DATA ·consts+0x20(SB)/8, $0x0000000000000004
|
||||
DATA ·consts+0x28(SB)/8, $0x0000000000000000
|
||||
DATA ·consts+0x30(SB)/8, $0x0a0b08090e0f0c0d
|
||||
DATA ·consts+0x38(SB)/8, $0x0203000106070405
|
||||
DATA ·consts+0x40(SB)/8, $0x090a0b080d0e0f0c
|
||||
DATA ·consts+0x48(SB)/8, $0x0102030005060704
|
||||
GLOBL ·consts(SB), RODATA, $80
|
||||
|
||||
//func chaCha20_ctr32_vmx(out, inp *byte, len int, key *[32]byte, counter *[16]byte)
|
||||
TEXT ·chaCha20_ctr32_vmx(SB),NOSPLIT|NOFRAME,$0
|
||||
// Load the arguments inside the registers
|
||||
MOVD out+0(FP), OUT
|
||||
MOVD inp+8(FP), INP
|
||||
MOVD len+16(FP), LEN
|
||||
MOVD key+24(FP), KEY
|
||||
MOVD counter+32(FP), CNT
|
||||
|
||||
MOVD $·consts(SB), CONSTS // point to consts addr
|
||||
|
||||
MOVD $16, X0
|
||||
MOVD $32, X1
|
||||
MOVD $48, X2
|
||||
MOVD $64, X3
|
||||
MOVD $31, X4
|
||||
MOVD $15, X5
|
||||
|
||||
// Load key
|
||||
LVX (KEY)(R0), K1
|
||||
LVSR (KEY)(R0), T0
|
||||
LVX (KEY)(X0), K2
|
||||
LVX (KEY)(X4), DD0
|
||||
|
||||
// Load counter
|
||||
LVX (CNT)(R0), K3
|
||||
LVSR (CNT)(R0), T1
|
||||
LVX (CNT)(X5), DD1
|
||||
|
||||
// Load constants
|
||||
LVX (CONSTS)(R0), K0
|
||||
LVX (CONSTS)(X0), K5
|
||||
LVX (CONSTS)(X1), FOUR
|
||||
LVX (CONSTS)(X2), SIXTEEN
|
||||
LVX (CONSTS)(X3), TWENTY4
|
||||
|
||||
// Align key and counter
|
||||
VPERM K2, K1, T0, K1
|
||||
VPERM DD0, K2, T0, K2
|
||||
VPERM DD1, K3, T1, K3
|
||||
|
||||
// Load counter to GPR
|
||||
MOVWZ 0(CNT), CNT0
|
||||
MOVWZ 4(CNT), CNT1
|
||||
MOVWZ 8(CNT), CNT2
|
||||
MOVWZ 12(CNT), CNT3
|
||||
|
||||
// Adjust vectors for the initial state
|
||||
VADDUWM K3, K5, K3
|
||||
VADDUWM K3, K5, K4
|
||||
VADDUWM K4, K5, K5
|
||||
|
||||
// Synthesized constants
|
||||
VSPLTISW $-12, TWENTY
|
||||
VSPLTISW $12, TWELVE
|
||||
VSPLTISW $-7, TWENTY5
|
||||
|
||||
VXOR T0, T0, T0
|
||||
VSPLTISW $-1, OUTMASK
|
||||
LVSR (INP)(R0), INPPERM
|
||||
LVSL (OUT)(R0), OUTPERM
|
||||
VPERM OUTMASK, T0, OUTPERM, OUTMASK
|
||||
|
||||
loop_outer_vmx:
|
||||
// Load constant
|
||||
MOVD $0x61707865, CON0
|
||||
MOVD $0x3320646e, CON1
|
||||
MOVD $0x79622d32, CON2
|
||||
MOVD $0x6b206574, CON3
|
||||
|
||||
VOR K0, K0, A0
|
||||
VOR K0, K0, A1
|
||||
VOR K0, K0, A2
|
||||
VOR K1, K1, B0
|
||||
|
||||
MOVD $10, TEMP
|
||||
|
||||
// Load key to GPR
|
||||
MOVWZ 0(KEY), X4
|
||||
MOVWZ 4(KEY), X5
|
||||
MOVWZ 8(KEY), X6
|
||||
MOVWZ 12(KEY), X7
|
||||
VOR K1, K1, B1
|
||||
VOR K1, K1, B2
|
||||
MOVWZ 16(KEY), X8
|
||||
MOVWZ 0(CNT), X12
|
||||
MOVWZ 20(KEY), X9
|
||||
MOVWZ 4(CNT), X13
|
||||
VOR K2, K2, C0
|
||||
VOR K2, K2, C1
|
||||
MOVWZ 24(KEY), X10
|
||||
MOVWZ 8(CNT), X14
|
||||
VOR K2, K2, C2
|
||||
VOR K3, K3, D0
|
||||
MOVWZ 28(KEY), X11
|
||||
MOVWZ 12(CNT), X15
|
||||
VOR K4, K4, D1
|
||||
VOR K5, K5, D2
|
||||
|
||||
MOVD X4, TMP0
|
||||
MOVD X5, TMP1
|
||||
MOVD X6, TMP2
|
||||
MOVD X7, TMP3
|
||||
VSPLTISW $7, SEVEN
|
||||
|
||||
MOVD TEMP, CTR
|
||||
|
||||
loop_vmx:
|
||||
// CRYPTOGAMS uses a macro to create a loop using perl. This isn't possible
|
||||
// using assembly macros. Therefore, the macro expansion result was used
|
||||
// in order to maintain the algorithm efficiency.
|
||||
// This loop generates three keystream blocks using VMX instructions and,
|
||||
// in parallel, one keystream block using scalar instructions.
|
||||
ADD X4, X0, X0
|
||||
ADD X5, X1, X1
|
||||
VADDUWM A0, B0, A0
|
||||
VADDUWM A1, B1, A1
|
||||
ADD X6, X2, X2
|
||||
ADD X7, X3, X3
|
||||
VADDUWM A2, B2, A2
|
||||
VXOR D0, A0, D0
|
||||
XOR X0, X12, X12
|
||||
XOR X1, X13, X13
|
||||
VXOR D1, A1, D1
|
||||
VXOR D2, A2, D2
|
||||
XOR X2, X14, X14
|
||||
XOR X3, X15, X15
|
||||
VPERM D0, D0, SIXTEEN, D0
|
||||
VPERM D1, D1, SIXTEEN, D1
|
||||
ROTLW $16, X12, X12
|
||||
ROTLW $16, X13, X13
|
||||
VPERM D2, D2, SIXTEEN, D2
|
||||
VADDUWM C0, D0, C0
|
||||
ROTLW $16, X14, X14
|
||||
ROTLW $16, X15, X15
|
||||
VADDUWM C1, D1, C1
|
||||
VADDUWM C2, D2, C2
|
||||
ADD X12, X8, X8
|
||||
ADD X13, X9, X9
|
||||
VXOR B0, C0, T0
|
||||
VXOR B1, C1, T1
|
||||
ADD X14, X10, X10
|
||||
ADD X15, X11, X11
|
||||
VXOR B2, C2, T2
|
||||
VRLW T0, TWELVE, B0
|
||||
XOR X8, X4, X4
|
||||
XOR X9, X5, X5
|
||||
VRLW T1, TWELVE, B1
|
||||
VRLW T2, TWELVE, B2
|
||||
XOR X10, X6, X6
|
||||
XOR X11, X7, X7
|
||||
VADDUWM A0, B0, A0
|
||||
VADDUWM A1, B1, A1
|
||||
ROTLW $12, X4, X4
|
||||
ROTLW $12, X5, X5
|
||||
VADDUWM A2, B2, A2
|
||||
VXOR D0, A0, D0
|
||||
ROTLW $12, X6, X6
|
||||
ROTLW $12, X7, X7
|
||||
VXOR D1, A1, D1
|
||||
VXOR D2, A2, D2
|
||||
ADD X4, X0, X0
|
||||
ADD X5, X1, X1
|
||||
VPERM D0, D0, TWENTY4, D0
|
||||
VPERM D1, D1, TWENTY4, D1
|
||||
ADD X6, X2, X2
|
||||
ADD X7, X3, X3
|
||||
VPERM D2, D2, TWENTY4, D2
|
||||
VADDUWM C0, D0, C0
|
||||
XOR X0, X12, X12
|
||||
XOR X1, X13, X13
|
||||
VADDUWM C1, D1, C1
|
||||
VADDUWM C2, D2, C2
|
||||
XOR X2, X14, X14
|
||||
XOR X3, X15, X15
|
||||
VXOR B0, C0, T0
|
||||
VXOR B1, C1, T1
|
||||
ROTLW $8, X12, X12
|
||||
ROTLW $8, X13, X13
|
||||
VXOR B2, C2, T2
|
||||
VRLW T0, SEVEN, B0
|
||||
ROTLW $8, X14, X14
|
||||
ROTLW $8, X15, X15
|
||||
VRLW T1, SEVEN, B1
|
||||
VRLW T2, SEVEN, B2
|
||||
ADD X12, X8, X8
|
||||
ADD X13, X9, X9
|
||||
VSLDOI $8, C0, C0, C0
|
||||
VSLDOI $8, C1, C1, C1
|
||||
ADD X14, X10, X10
|
||||
ADD X15, X11, X11
|
||||
VSLDOI $8, C2, C2, C2
|
||||
VSLDOI $12, B0, B0, B0
|
||||
XOR X8, X4, X4
|
||||
XOR X9, X5, X5
|
||||
VSLDOI $12, B1, B1, B1
|
||||
VSLDOI $12, B2, B2, B2
|
||||
XOR X10, X6, X6
|
||||
XOR X11, X7, X7
|
||||
VSLDOI $4, D0, D0, D0
|
||||
VSLDOI $4, D1, D1, D1
|
||||
ROTLW $7, X4, X4
|
||||
ROTLW $7, X5, X5
|
||||
VSLDOI $4, D2, D2, D2
|
||||
VADDUWM A0, B0, A0
|
||||
ROTLW $7, X6, X6
|
||||
ROTLW $7, X7, X7
|
||||
VADDUWM A1, B1, A1
|
||||
VADDUWM A2, B2, A2
|
||||
ADD X5, X0, X0
|
||||
ADD X6, X1, X1
|
||||
VXOR D0, A0, D0
|
||||
VXOR D1, A1, D1
|
||||
ADD X7, X2, X2
|
||||
ADD X4, X3, X3
|
||||
VXOR D2, A2, D2
|
||||
VPERM D0, D0, SIXTEEN, D0
|
||||
XOR X0, X15, X15
|
||||
XOR X1, X12, X12
|
||||
VPERM D1, D1, SIXTEEN, D1
|
||||
VPERM D2, D2, SIXTEEN, D2
|
||||
XOR X2, X13, X13
|
||||
XOR X3, X14, X14
|
||||
VADDUWM C0, D0, C0
|
||||
VADDUWM C1, D1, C1
|
||||
ROTLW $16, X15, X15
|
||||
ROTLW $16, X12, X12
|
||||
VADDUWM C2, D2, C2
|
||||
VXOR B0, C0, T0
|
||||
ROTLW $16, X13, X13
|
||||
ROTLW $16, X14, X14
|
||||
VXOR B1, C1, T1
|
||||
VXOR B2, C2, T2
|
||||
ADD X15, X10, X10
|
||||
ADD X12, X11, X11
|
||||
VRLW T0, TWELVE, B0
|
||||
VRLW T1, TWELVE, B1
|
||||
ADD X13, X8, X8
|
||||
ADD X14, X9, X9
|
||||
VRLW T2, TWELVE, B2
|
||||
VADDUWM A0, B0, A0
|
||||
XOR X10, X5, X5
|
||||
XOR X11, X6, X6
|
||||
VADDUWM A1, B1, A1
|
||||
VADDUWM A2, B2, A2
|
||||
XOR X8, X7, X7
|
||||
XOR X9, X4, X4
|
||||
VXOR D0, A0, D0
|
||||
VXOR D1, A1, D1
|
||||
ROTLW $12, X5, X5
|
||||
ROTLW $12, X6, X6
|
||||
VXOR D2, A2, D2
|
||||
VPERM D0, D0, TWENTY4, D0
|
||||
ROTLW $12, X7, X7
|
||||
ROTLW $12, X4, X4
|
||||
VPERM D1, D1, TWENTY4, D1
|
||||
VPERM D2, D2, TWENTY4, D2
|
||||
ADD X5, X0, X0
|
||||
ADD X6, X1, X1
|
||||
VADDUWM C0, D0, C0
|
||||
VADDUWM C1, D1, C1
|
||||
ADD X7, X2, X2
|
||||
ADD X4, X3, X3
|
||||
VADDUWM C2, D2, C2
|
||||
VXOR B0, C0, T0
|
||||
XOR X0, X15, X15
|
||||
XOR X1, X12, X12
|
||||
VXOR B1, C1, T1
|
||||
VXOR B2, C2, T2
|
||||
XOR X2, X13, X13
|
||||
XOR X3, X14, X14
|
||||
VRLW T0, SEVEN, B0
|
||||
VRLW T1, SEVEN, B1
|
||||
ROTLW $8, X15, X15
|
||||
ROTLW $8, X12, X12
|
||||
VRLW T2, SEVEN, B2
|
||||
VSLDOI $8, C0, C0, C0
|
||||
ROTLW $8, X13, X13
|
||||
ROTLW $8, X14, X14
|
||||
VSLDOI $8, C1, C1, C1
|
||||
VSLDOI $8, C2, C2, C2
|
||||
ADD X15, X10, X10
|
||||
ADD X12, X11, X11
|
||||
VSLDOI $4, B0, B0, B0
|
||||
VSLDOI $4, B1, B1, B1
|
||||
ADD X13, X8, X8
|
||||
ADD X14, X9, X9
|
||||
VSLDOI $4, B2, B2, B2
|
||||
VSLDOI $12, D0, D0, D0
|
||||
XOR X10, X5, X5
|
||||
XOR X11, X6, X6
|
||||
VSLDOI $12, D1, D1, D1
|
||||
VSLDOI $12, D2, D2, D2
|
||||
XOR X8, X7, X7
|
||||
XOR X9, X4, X4
|
||||
ROTLW $7, X5, X5
|
||||
ROTLW $7, X6, X6
|
||||
ROTLW $7, X7, X7
|
||||
ROTLW $7, X4, X4
|
||||
BC 0x10, 0, loop_vmx
|
||||
|
||||
SUB $256, LEN, LEN
|
||||
|
||||
// Accumulate key block
|
||||
ADD $0x61707865, X0, X0
|
||||
ADD $0x3320646e, X1, X1
|
||||
ADD $0x79622d32, X2, X2
|
||||
ADD $0x6b206574, X3, X3
|
||||
ADD TMP0, X4, X4
|
||||
ADD TMP1, X5, X5
|
||||
ADD TMP2, X6, X6
|
||||
ADD TMP3, X7, X7
|
||||
MOVWZ 16(KEY), TMP0
|
||||
MOVWZ 20(KEY), TMP1
|
||||
MOVWZ 24(KEY), TMP2
|
||||
MOVWZ 28(KEY), TMP3
|
||||
ADD TMP0, X8, X8
|
||||
ADD TMP1, X9, X9
|
||||
ADD TMP2, X10, X10
|
||||
ADD TMP3, X11, X11
|
||||
|
||||
MOVWZ 12(CNT), TMP0
|
||||
MOVWZ 8(CNT), TMP1
|
||||
MOVWZ 4(CNT), TMP2
|
||||
MOVWZ 0(CNT), TEMP
|
||||
ADD TMP0, X15, X15
|
||||
ADD TMP1, X14, X14
|
||||
ADD TMP2, X13, X13
|
||||
ADD TEMP, X12, X12
|
||||
|
||||
// Accumulate key block
|
||||
VADDUWM A0, K0, A0
|
||||
VADDUWM A1, K0, A1
|
||||
VADDUWM A2, K0, A2
|
||||
VADDUWM B0, K1, B0
|
||||
VADDUWM B1, K1, B1
|
||||
VADDUWM B2, K1, B2
|
||||
VADDUWM C0, K2, C0
|
||||
VADDUWM C1, K2, C1
|
||||
VADDUWM C2, K2, C2
|
||||
VADDUWM D0, K3, D0
|
||||
VADDUWM D1, K4, D1
|
||||
VADDUWM D2, K5, D2
|
||||
|
||||
// Increment counter
|
||||
ADD $4, TEMP, TEMP
|
||||
MOVW TEMP, 0(CNT)
|
||||
|
||||
VADDUWM K3, FOUR, K3
|
||||
VADDUWM K4, FOUR, K4
|
||||
VADDUWM K5, FOUR, K5
|
||||
|
||||
// XOR the input slice (INP) with the keystream, which is stored in GPRs (X0-X3).
|
||||
|
||||
// Load input (aligned or not)
|
||||
MOVWZ 0(INP), TMP0
|
||||
MOVWZ 4(INP), TMP1
|
||||
MOVWZ 8(INP), TMP2
|
||||
MOVWZ 12(INP), TMP3
|
||||
|
||||
// XOR with input
|
||||
XOR TMP0, X0, X0
|
||||
XOR TMP1, X1, X1
|
||||
XOR TMP2, X2, X2
|
||||
XOR TMP3, X3, X3
|
||||
MOVWZ 16(INP), TMP0
|
||||
MOVWZ 20(INP), TMP1
|
||||
MOVWZ 24(INP), TMP2
|
||||
MOVWZ 28(INP), TMP3
|
||||
XOR TMP0, X4, X4
|
||||
XOR TMP1, X5, X5
|
||||
XOR TMP2, X6, X6
|
||||
XOR TMP3, X7, X7
|
||||
MOVWZ 32(INP), TMP0
|
||||
MOVWZ 36(INP), TMP1
|
||||
MOVWZ 40(INP), TMP2
|
||||
MOVWZ 44(INP), TMP3
|
||||
XOR TMP0, X8, X8
|
||||
XOR TMP1, X9, X9
|
||||
XOR TMP2, X10, X10
|
||||
XOR TMP3, X11, X11
|
||||
MOVWZ 48(INP), TMP0
|
||||
MOVWZ 52(INP), TMP1
|
||||
MOVWZ 56(INP), TMP2
|
||||
MOVWZ 60(INP), TMP3
|
||||
XOR TMP0, X12, X12
|
||||
XOR TMP1, X13, X13
|
||||
XOR TMP2, X14, X14
|
||||
XOR TMP3, X15, X15
|
||||
|
||||
// Store output (aligned or not)
|
||||
MOVW X0, 0(OUT)
|
||||
MOVW X1, 4(OUT)
|
||||
MOVW X2, 8(OUT)
|
||||
MOVW X3, 12(OUT)
|
||||
|
||||
ADD $64, INP, INP // INP points to the end of the slice for the alignment code below
|
||||
|
||||
MOVW X4, 16(OUT)
|
||||
MOVD $16, TMP0
|
||||
MOVW X5, 20(OUT)
|
||||
MOVD $32, TMP1
|
||||
MOVW X6, 24(OUT)
|
||||
MOVD $48, TMP2
|
||||
MOVW X7, 28(OUT)
|
||||
MOVD $64, TMP3
|
||||
MOVW X8, 32(OUT)
|
||||
MOVW X9, 36(OUT)
|
||||
MOVW X10, 40(OUT)
|
||||
MOVW X11, 44(OUT)
|
||||
MOVW X12, 48(OUT)
|
||||
MOVW X13, 52(OUT)
|
||||
MOVW X14, 56(OUT)
|
||||
MOVW X15, 60(OUT)
|
||||
ADD $64, OUT, OUT
|
||||
|
||||
// Load input
|
||||
LVX (INP)(R0), DD0
|
||||
LVX (INP)(TMP0), DD1
|
||||
LVX (INP)(TMP1), DD2
|
||||
LVX (INP)(TMP2), DD3
|
||||
LVX (INP)(TMP3), DD4
|
||||
ADD $64, INP, INP
|
||||
|
||||
VPERM DD1, DD0, INPPERM, DD0 // Align input
|
||||
VPERM DD2, DD1, INPPERM, DD1
|
||||
VPERM DD3, DD2, INPPERM, DD2
|
||||
VPERM DD4, DD3, INPPERM, DD3
|
||||
VXOR A0, DD0, A0 // XOR with input
|
||||
VXOR B0, DD1, B0
|
||||
LVX (INP)(TMP0), DD1 // Keep loading input
|
||||
VXOR C0, DD2, C0
|
||||
LVX (INP)(TMP1), DD2
|
||||
VXOR D0, DD3, D0
|
||||
LVX (INP)(TMP2), DD3
|
||||
LVX (INP)(TMP3), DD0
|
||||
ADD $64, INP, INP
|
||||
MOVD $63, TMP3 // 63 is not a typo
|
||||
VPERM A0, A0, OUTPERM, A0
|
||||
VPERM B0, B0, OUTPERM, B0
|
||||
VPERM C0, C0, OUTPERM, C0
|
||||
VPERM D0, D0, OUTPERM, D0
|
||||
|
||||
VPERM DD1, DD4, INPPERM, DD4 // Align input
|
||||
VPERM DD2, DD1, INPPERM, DD1
|
||||
VPERM DD3, DD2, INPPERM, DD2
|
||||
VPERM DD0, DD3, INPPERM, DD3
|
||||
VXOR A1, DD4, A1
|
||||
VXOR B1, DD1, B1
|
||||
LVX (INP)(TMP0), DD1 // Keep loading
|
||||
VXOR C1, DD2, C1
|
||||
LVX (INP)(TMP1), DD2
|
||||
VXOR D1, DD3, D1
|
||||
LVX (INP)(TMP2), DD3
|
||||
|
||||
// Note that the LVX address is always rounded down to the nearest 16-byte
|
||||
// boundary, and that it always points to at most 15 bytes beyond the end of
|
||||
// the slice, so we cannot cross a page boundary.
|
||||
LVX (INP)(TMP3), DD4 // Redundant in aligned case.
|
||||
ADD $64, INP, INP
|
||||
VPERM A1, A1, OUTPERM, A1 // Pre-misalign output
|
||||
VPERM B1, B1, OUTPERM, B1
|
||||
VPERM C1, C1, OUTPERM, C1
|
||||
VPERM D1, D1, OUTPERM, D1
|
||||
|
||||
VPERM DD1, DD0, INPPERM, DD0 // Align Input
|
||||
VPERM DD2, DD1, INPPERM, DD1
|
||||
VPERM DD3, DD2, INPPERM, DD2
|
||||
VPERM DD4, DD3, INPPERM, DD3
|
||||
VXOR A2, DD0, A2
|
||||
VXOR B2, DD1, B2
|
||||
VXOR C2, DD2, C2
|
||||
VXOR D2, DD3, D2
|
||||
VPERM A2, A2, OUTPERM, A2
|
||||
VPERM B2, B2, OUTPERM, B2
|
||||
VPERM C2, C2, OUTPERM, C2
|
||||
VPERM D2, D2, OUTPERM, D2
|
||||
|
||||
ANDCC $15, OUT, X1 // Is out aligned?
|
||||
MOVD OUT, X0
|
||||
|
||||
VSEL A0, B0, OUTMASK, DD0 // Collect pre-misaligned output
|
||||
VSEL B0, C0, OUTMASK, DD1
|
||||
VSEL C0, D0, OUTMASK, DD2
|
||||
VSEL D0, A1, OUTMASK, DD3
|
||||
VSEL A1, B1, OUTMASK, B0
|
||||
VSEL B1, C1, OUTMASK, C0
|
||||
VSEL C1, D1, OUTMASK, D0
|
||||
VSEL D1, A2, OUTMASK, A1
|
||||
VSEL A2, B2, OUTMASK, B1
|
||||
VSEL B2, C2, OUTMASK, C1
|
||||
VSEL C2, D2, OUTMASK, D1
|
||||
|
||||
STVX DD0, (OUT+TMP0)
|
||||
STVX DD1, (OUT+TMP1)
|
||||
STVX DD2, (OUT+TMP2)
|
||||
ADD $64, OUT, OUT
|
||||
STVX DD3, (OUT+R0)
|
||||
STVX B0, (OUT+TMP0)
|
||||
STVX C0, (OUT+TMP1)
|
||||
STVX D0, (OUT+TMP2)
|
||||
ADD $64, OUT, OUT
|
||||
STVX A1, (OUT+R0)
|
||||
STVX B1, (OUT+TMP0)
|
||||
STVX C1, (OUT+TMP1)
|
||||
STVX D1, (OUT+TMP2)
|
||||
ADD $64, OUT, OUT
|
||||
|
||||
BEQ aligned_vmx
|
||||
|
||||
SUB X1, OUT, X2 // in misaligned case edges
|
||||
MOVD $0, X3 // are written byte-by-byte
|
||||
|
||||
unaligned_tail_vmx:
|
||||
STVEBX D2, (X2+X3)
|
||||
ADD $1, X3, X3
|
||||
CMPW X3, X1
|
||||
BNE unaligned_tail_vmx
|
||||
SUB X1, X0, X2
|
||||
|
||||
unaligned_head_vmx:
|
||||
STVEBX A0, (X2+X1)
|
||||
CMPW X1, $15
|
||||
ADD $1, X1, X1
|
||||
BNE unaligned_head_vmx
|
||||
|
||||
CMPU LEN, $255 // done with 256-byte block yet?
|
||||
BGT loop_outer_vmx
|
||||
|
||||
JMP done_vmx
|
||||
|
||||
aligned_vmx:
|
||||
STVX A0, (X0+R0)
|
||||
CMPU LEN, $255 // done with 256-byte block yet?
|
||||
BGT loop_outer_vmx
|
||||
|
||||
done_vmx:
|
||||
RET
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !arm64,!s390x arm64,!go1.11 gccgo appengine
|
||||
// +build !ppc64le,!arm64,!s390x arm64,!go1.11 gccgo appengine
|
||||
|
||||
package chacha20
|
||||
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ppc64le,!gccgo,!appengine
|
||||
|
||||
package chacha20
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
bufSize = 256
|
||||
haveAsm = true
|
||||
)
|
||||
|
||||
//go:noescape
|
||||
func chaCha20_ctr32_vmx(out, inp *byte, len int, key *[8]uint32, counter *uint32)
|
||||
|
||||
func (c *Cipher) xorKeyStreamAsm(dst, src []byte) {
|
||||
if len(src) >= bufSize {
|
||||
chaCha20_ctr32_vmx(&dst[0], &src[0], len(src)-len(src)%bufSize, &c.key, &c.counter)
|
||||
}
|
||||
if len(src)%bufSize != 0 {
|
||||
chaCha20_ctr32_vmx(&c.buf[0], &c.buf[0], bufSize, &c.key, &c.counter)
|
||||
start := len(src) - len(src)%bufSize
|
||||
ts, td, tb := src[start:], dst[start:], c.buf[:]
|
||||
// Unroll loop to XOR 32 bytes per iteration.
|
||||
for i := 0; i < len(ts)-32; i += 32 {
|
||||
td, tb = td[:len(ts)], tb[:len(ts)] // bounds check elimination
|
||||
s0 := binary.LittleEndian.Uint64(ts[0:8])
|
||||
s1 := binary.LittleEndian.Uint64(ts[8:16])
|
||||
s2 := binary.LittleEndian.Uint64(ts[16:24])
|
||||
s3 := binary.LittleEndian.Uint64(ts[24:32])
|
||||
b0 := binary.LittleEndian.Uint64(tb[0:8])
|
||||
b1 := binary.LittleEndian.Uint64(tb[8:16])
|
||||
b2 := binary.LittleEndian.Uint64(tb[16:24])
|
||||
b3 := binary.LittleEndian.Uint64(tb[24:32])
|
||||
binary.LittleEndian.PutUint64(td[0:8], s0^b0)
|
||||
binary.LittleEndian.PutUint64(td[8:16], s1^b1)
|
||||
binary.LittleEndian.PutUint64(td[16:24], s2^b2)
|
||||
binary.LittleEndian.PutUint64(td[24:32], s3^b3)
|
||||
ts, td, tb = ts[32:], td[32:], tb[32:]
|
||||
}
|
||||
td, tb = td[:len(ts)], tb[:len(ts)] // bounds check elimination
|
||||
for i, v := range ts {
|
||||
td[i] = tb[i] ^ v
|
||||
}
|
||||
c.len = bufSize - (len(src) % bufSize)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64 gccgo appengine
|
||||
// +build !amd64,!ppc64le gccgo appengine
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build s390x,!go1.11 !arm,!amd64,!s390x gccgo appengine nacl
|
||||
// +build s390x,!go1.11 !arm,!amd64,!s390x,!ppc64le gccgo appengine nacl
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ppc64le,!gccgo,!appengine
|
||||
|
||||
package poly1305
|
||||
|
||||
//go:noescape
|
||||
func initialize(state *[7]uint64, key *[32]byte)
|
||||
|
||||
//go:noescape
|
||||
func update(state *[7]uint64, msg []byte)
|
||||
|
||||
//go:noescape
|
||||
func finalize(tag *[TagSize]byte, state *[7]uint64)
|
||||
|
||||
// Sum generates an authenticator for m using a one-time key and puts the
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[16]byte, m []byte, key *[32]byte) {
|
||||
h := newMAC(key)
|
||||
h.Write(m)
|
||||
h.Sum(out)
|
||||
}
|
||||
|
||||
func newMAC(key *[32]byte) (h mac) {
|
||||
initialize(&h.state, key)
|
||||
return
|
||||
}
|
||||
|
||||
type mac struct {
|
||||
state [7]uint64 // := uint64{ h0, h1, h2, r0, r1, pad0, pad1 }
|
||||
|
||||
buffer [TagSize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (h *mac) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
if h.offset > 0 {
|
||||
remaining := TagSize - h.offset
|
||||
if n < remaining {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
return n, nil
|
||||
}
|
||||
copy(h.buffer[h.offset:], p[:remaining])
|
||||
p = p[remaining:]
|
||||
h.offset = 0
|
||||
update(&h.state, h.buffer[:])
|
||||
}
|
||||
if nn := len(p) - (len(p) % TagSize); nn > 0 {
|
||||
update(&h.state, p[:nn])
|
||||
p = p[nn:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (h *mac) Sum(out *[16]byte) {
|
||||
state := h.state
|
||||
if h.offset > 0 {
|
||||
update(&state, h.buffer[:h.offset])
|
||||
}
|
||||
finalize(out, &state)
|
||||
}
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ppc64le,!gccgo,!appengine
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// This was ported from the amd64 implementation.
|
||||
|
||||
#define POLY1305_ADD(msg, h0, h1, h2, t0, t1, t2) \
|
||||
MOVD (msg), t0; \
|
||||
MOVD 8(msg), t1; \
|
||||
MOVD $1, t2; \
|
||||
ADDC t0, h0, h0; \
|
||||
ADDE t1, h1, h1; \
|
||||
ADDE t2, h2; \
|
||||
ADD $16, msg
|
||||
|
||||
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3, t4, t5) \
|
||||
MULLD r0, h0, t0; \
|
||||
MULLD r0, h1, t4; \
|
||||
MULHDU r0, h0, t1; \
|
||||
MULHDU r0, h1, t5; \
|
||||
ADDC t4, t1, t1; \
|
||||
MULLD r0, h2, t2; \
|
||||
ADDZE t5; \
|
||||
MULHDU r1, h0, t4; \
|
||||
MULLD r1, h0, h0; \
|
||||
ADD t5, t2, t2; \
|
||||
ADDC h0, t1, t1; \
|
||||
MULLD h2, r1, t3; \
|
||||
ADDZE t4, h0; \
|
||||
MULHDU r1, h1, t5; \
|
||||
MULLD r1, h1, t4; \
|
||||
ADDC t4, t2, t2; \
|
||||
ADDE t5, t3, t3; \
|
||||
ADDC h0, t2, t2; \
|
||||
MOVD $-4, t4; \
|
||||
MOVD t0, h0; \
|
||||
MOVD t1, h1; \
|
||||
ADDZE t3; \
|
||||
ANDCC $3, t2, h2; \
|
||||
AND t2, t4, t0; \
|
||||
ADDC t0, h0, h0; \
|
||||
ADDE t3, h1, h1; \
|
||||
SLD $62, t3, t4; \
|
||||
SRD $2, t2; \
|
||||
ADDZE h2; \
|
||||
OR t4, t2, t2; \
|
||||
SRD $2, t3; \
|
||||
ADDC t2, h0, h0; \
|
||||
ADDE t3, h1, h1; \
|
||||
ADDZE h2
|
||||
|
||||
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
|
||||
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
|
||||
GLOBL ·poly1305Mask<>(SB), RODATA, $16
|
||||
|
||||
// func update(state *[7]uint64, msg []byte)
|
||||
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVD state+0(FP), R3
|
||||
MOVD msg_base+8(FP), R4
|
||||
MOVD msg_len+16(FP), R5
|
||||
|
||||
MOVD 0(R3), R8 // h0
|
||||
MOVD 8(R3), R9 // h1
|
||||
MOVD 16(R3), R10 // h2
|
||||
MOVD 24(R3), R11 // r0
|
||||
MOVD 32(R3), R12 // r1
|
||||
|
||||
CMP R5, $16
|
||||
BLT bytes_between_0_and_15
|
||||
|
||||
loop:
|
||||
POLY1305_ADD(R4, R8, R9, R10, R20, R21, R22)
|
||||
|
||||
multiply:
|
||||
POLY1305_MUL(R8, R9, R10, R11, R12, R16, R17, R18, R14, R20, R21)
|
||||
ADD $-16, R5
|
||||
CMP R5, $16
|
||||
BGE loop
|
||||
|
||||
bytes_between_0_and_15:
|
||||
CMP $0, R5
|
||||
BEQ done
|
||||
MOVD $0, R16 // h0
|
||||
MOVD $0, R17 // h1
|
||||
|
||||
flush_buffer:
|
||||
CMP R5, $8
|
||||
BLE just1
|
||||
|
||||
MOVD $8, R21
|
||||
SUB R21, R5, R21
|
||||
|
||||
// Greater than 8 -- load the rightmost remaining bytes in msg
|
||||
// and put into R17 (h1)
|
||||
MOVD (R4)(R21), R17
|
||||
MOVD $16, R22
|
||||
|
||||
// Find the offset to those bytes
|
||||
SUB R5, R22, R22
|
||||
SLD $3, R22
|
||||
|
||||
// Shift to get only the bytes in msg
|
||||
SRD R22, R17, R17
|
||||
|
||||
// Put 1 at high end
|
||||
MOVD $1, R23
|
||||
SLD $3, R21
|
||||
SLD R21, R23, R23
|
||||
OR R23, R17, R17
|
||||
|
||||
// Remainder is 8
|
||||
MOVD $8, R5
|
||||
|
||||
just1:
|
||||
CMP R5, $8
|
||||
BLT less8
|
||||
|
||||
// Exactly 8
|
||||
MOVD (R4), R16
|
||||
|
||||
CMP $0, R17
|
||||
|
||||
// Check if we've already set R17; if not
|
||||
// set 1 to indicate end of msg.
|
||||
BNE carry
|
||||
MOVD $1, R17
|
||||
BR carry
|
||||
|
||||
less8:
|
||||
MOVD $0, R16 // h0
|
||||
MOVD $0, R22 // shift count
|
||||
CMP R5, $4
|
||||
BLT less4
|
||||
MOVWZ (R4), R16
|
||||
ADD $4, R4
|
||||
ADD $-4, R5
|
||||
MOVD $32, R22
|
||||
|
||||
less4:
|
||||
CMP R5, $2
|
||||
BLT less2
|
||||
MOVHZ (R4), R21
|
||||
SLD R22, R21, R21
|
||||
OR R16, R21, R16
|
||||
ADD $16, R22
|
||||
ADD $-2, R5
|
||||
ADD $2, R4
|
||||
|
||||
less2:
|
||||
CMP $0, R5
|
||||
BEQ insert1
|
||||
MOVBZ (R4), R21
|
||||
SLD R22, R21, R21
|
||||
OR R16, R21, R16
|
||||
ADD $8, R22
|
||||
|
||||
insert1:
|
||||
// Insert 1 at end of msg
|
||||
MOVD $1, R21
|
||||
SLD R22, R21, R21
|
||||
OR R16, R21, R16
|
||||
|
||||
carry:
|
||||
// Add new values to h0, h1, h2
|
||||
ADDC R16, R8
|
||||
ADDE R17, R9
|
||||
ADDE $0, R10
|
||||
MOVD $16, R5
|
||||
ADD R5, R4
|
||||
BR multiply
|
||||
|
||||
done:
|
||||
// Save h0, h1, h2 in state
|
||||
MOVD R8, 0(R3)
|
||||
MOVD R9, 8(R3)
|
||||
MOVD R10, 16(R3)
|
||||
RET
|
||||
|
||||
// func initialize(state *[7]uint64, key *[32]byte)
|
||||
TEXT ·initialize(SB), $0-16
|
||||
MOVD state+0(FP), R3
|
||||
MOVD key+8(FP), R4
|
||||
|
||||
// state[0...7] is initialized with zero
|
||||
// Load key
|
||||
MOVD 0(R4), R5
|
||||
MOVD 8(R4), R6
|
||||
MOVD 16(R4), R7
|
||||
MOVD 24(R4), R8
|
||||
|
||||
// Address of key mask
|
||||
MOVD $·poly1305Mask<>(SB), R9
|
||||
|
||||
// Save original key in state
|
||||
MOVD R7, 40(R3)
|
||||
MOVD R8, 48(R3)
|
||||
|
||||
// Get mask
|
||||
MOVD (R9), R7
|
||||
MOVD 8(R9), R8
|
||||
|
||||
// And with key
|
||||
AND R5, R7, R5
|
||||
AND R6, R8, R6
|
||||
|
||||
// Save masked key in state
|
||||
MOVD R5, 24(R3)
|
||||
MOVD R6, 32(R3)
|
||||
RET
|
||||
|
||||
// func finalize(tag *[TagSize]byte, state *[7]uint64)
|
||||
TEXT ·finalize(SB), $0-16
|
||||
MOVD tag+0(FP), R3
|
||||
MOVD state+8(FP), R4
|
||||
|
||||
// Get h0, h1, h2 from state
|
||||
MOVD 0(R4), R5
|
||||
MOVD 8(R4), R6
|
||||
MOVD 16(R4), R7
|
||||
|
||||
// Save h0, h1
|
||||
MOVD R5, R8
|
||||
MOVD R6, R9
|
||||
MOVD $3, R20
|
||||
MOVD $-1, R21
|
||||
SUBC $-5, R5
|
||||
SUBE R21, R6
|
||||
SUBE R20, R7
|
||||
MOVD $0, R21
|
||||
SUBZE R21
|
||||
|
||||
// Check for carry
|
||||
CMP $0, R21
|
||||
ISEL $2, R5, R8, R5
|
||||
ISEL $2, R6, R9, R6
|
||||
MOVD 40(R4), R8
|
||||
MOVD 48(R4), R9
|
||||
ADDC R8, R5
|
||||
ADDE R9, R6
|
||||
MOVD R5, 0(R3)
|
||||
MOVD R6, 8(R3)
|
||||
RET
|
||||
+12
-12
@@ -149,8 +149,8 @@ type streamPacketCipher struct {
|
||||
macResult []byte
|
||||
}
|
||||
|
||||
// readPacket reads and decrypt a single packet from the reader argument.
|
||||
func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
// readCipherPacket reads and decrypt a single packet from the reader argument.
|
||||
func (s *streamPacketCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
if _, err := io.ReadFull(r, s.prefix[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -221,8 +221,8 @@ func (s *streamPacketCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, err
|
||||
return s.packetData[:length-paddingLength-1], nil
|
||||
}
|
||||
|
||||
// writePacket encrypts and sends a packet of data to the writer argument
|
||||
func (s *streamPacketCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
|
||||
// writeCipherPacket encrypts and sends a packet of data to the writer argument
|
||||
func (s *streamPacketCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
|
||||
if len(packet) > maxPacket {
|
||||
return errors.New("ssh: packet too large")
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms)
|
||||
|
||||
const gcmTagSize = 16
|
||||
|
||||
func (c *gcmCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
|
||||
func (c *gcmCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
|
||||
// Pad out to multiple of 16 bytes. This is different from the
|
||||
// stream cipher because that encrypts the length too.
|
||||
padding := byte(packetSizeMultiple - (1+len(packet))%packetSizeMultiple)
|
||||
@@ -370,7 +370,7 @@ func (c *gcmCipher) incIV() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
func (c *gcmCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
if _, err := io.ReadFull(r, c.prefix[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -486,8 +486,8 @@ type cbcError string
|
||||
|
||||
func (e cbcError) Error() string { return string(e) }
|
||||
|
||||
func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
p, err := c.readPacketLeaky(seqNum, r)
|
||||
func (c *cbcCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
p, err := c.readCipherPacketLeaky(seqNum, r)
|
||||
if err != nil {
|
||||
if _, ok := err.(cbcError); ok {
|
||||
// Verification error: read a fixed amount of
|
||||
@@ -500,7 +500,7 @@ func (c *cbcCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
return p, err
|
||||
}
|
||||
|
||||
func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
func (c *cbcCipher) readCipherPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
blockSize := c.decrypter.BlockSize()
|
||||
|
||||
// Read the header, which will include some of the subsequent data in the
|
||||
@@ -576,7 +576,7 @@ func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error)
|
||||
return c.packetData[prefixLen:paddingStart], nil
|
||||
}
|
||||
|
||||
func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
|
||||
func (c *cbcCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, packet []byte) error {
|
||||
effectiveBlockSize := maxUInt32(cbcMinPacketSizeMultiple, c.encrypter.BlockSize())
|
||||
|
||||
// Length of encrypted portion of the packet (header, payload, padding).
|
||||
@@ -665,7 +665,7 @@ func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionA
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *chacha20Poly1305Cipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
func (c *chacha20Poly1305Cipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) {
|
||||
nonce := [3]uint32{0, 0, bits.ReverseBytes32(seqNum)}
|
||||
s := chacha20.New(c.contentKey, nonce)
|
||||
var polyKey [32]byte
|
||||
@@ -723,7 +723,7 @@ func (c *chacha20Poly1305Cipher) readPacket(seqNum uint32, r io.Reader) ([]byte,
|
||||
return plain, nil
|
||||
}
|
||||
|
||||
func (c *chacha20Poly1305Cipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error {
|
||||
func (c *chacha20Poly1305Cipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error {
|
||||
nonce := [3]uint32{0, 0, bits.ReverseBytes32(seqNum)}
|
||||
s := chacha20.New(c.contentKey, nonce)
|
||||
var polyKey [32]byte
|
||||
|
||||
+114
@@ -523,3 +523,117 @@ func (r *retryableAuthMethod) method() string {
|
||||
func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
|
||||
return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
|
||||
}
|
||||
|
||||
// GSSAPIWithMICAuthMethod is an AuthMethod with "gssapi-with-mic" authentication.
|
||||
// See RFC 4462 section 3
|
||||
// gssAPIClient is implementation of the GSSAPIClient interface, see the definition of the interface for details.
|
||||
// target is the server host you want to log in to.
|
||||
func GSSAPIWithMICAuthMethod(gssAPIClient GSSAPIClient, target string) AuthMethod {
|
||||
if gssAPIClient == nil {
|
||||
panic("gss-api client must be not nil with enable gssapi-with-mic")
|
||||
}
|
||||
return &gssAPIWithMICCallback{gssAPIClient: gssAPIClient, target: target}
|
||||
}
|
||||
|
||||
type gssAPIWithMICCallback struct {
|
||||
gssAPIClient GSSAPIClient
|
||||
target string
|
||||
}
|
||||
|
||||
func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
|
||||
m := &userAuthRequestMsg{
|
||||
User: user,
|
||||
Service: serviceSSH,
|
||||
Method: g.method(),
|
||||
}
|
||||
// The GSS-API authentication method is initiated when the client sends an SSH_MSG_USERAUTH_REQUEST.
|
||||
// See RFC 4462 section 3.2.
|
||||
m.Payload = appendU32(m.Payload, 1)
|
||||
m.Payload = appendString(m.Payload, string(krb5OID))
|
||||
if err := c.writePacket(Marshal(m)); err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
// The server responds to the SSH_MSG_USERAUTH_REQUEST with either an
|
||||
// SSH_MSG_USERAUTH_FAILURE if none of the mechanisms are supported or
|
||||
// with an SSH_MSG_USERAUTH_GSSAPI_RESPONSE.
|
||||
// See RFC 4462 section 3.3.
|
||||
// OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,so I don't want to check
|
||||
// selected mech if it is valid.
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
userAuthGSSAPIResp := &userAuthGSSAPIResponse{}
|
||||
if err := Unmarshal(packet, userAuthGSSAPIResp); err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
// Start the loop into the exchange token.
|
||||
// See RFC 4462 section 3.4.
|
||||
var token []byte
|
||||
defer g.gssAPIClient.DeleteSecContext()
|
||||
for {
|
||||
// Initiates the establishment of a security context between the application and a remote peer.
|
||||
nextToken, needContinue, err := g.gssAPIClient.InitSecContext("host@"+g.target, token, false)
|
||||
if err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
if len(nextToken) > 0 {
|
||||
if err := c.writePacket(Marshal(&userAuthGSSAPIToken{
|
||||
Token: nextToken,
|
||||
})); err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
}
|
||||
if !needContinue {
|
||||
break
|
||||
}
|
||||
packet, err = c.readPacket()
|
||||
if err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
switch packet[0] {
|
||||
case msgUserAuthFailure:
|
||||
var msg userAuthFailureMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
if msg.PartialSuccess {
|
||||
return authPartialSuccess, msg.Methods, nil
|
||||
}
|
||||
return authFailure, msg.Methods, nil
|
||||
case msgUserAuthGSSAPIError:
|
||||
userAuthGSSAPIErrorResp := &userAuthGSSAPIError{}
|
||||
if err := Unmarshal(packet, userAuthGSSAPIErrorResp); err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
return authFailure, nil, fmt.Errorf("GSS-API Error:\n"+
|
||||
"Major Status: %d\n"+
|
||||
"Minor Status: %d\n"+
|
||||
"Error Message: %s\n", userAuthGSSAPIErrorResp.MajorStatus, userAuthGSSAPIErrorResp.MinorStatus,
|
||||
userAuthGSSAPIErrorResp.Message)
|
||||
case msgUserAuthGSSAPIToken:
|
||||
userAuthGSSAPITokenReq := &userAuthGSSAPIToken{}
|
||||
if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
token = userAuthGSSAPITokenReq.Token
|
||||
}
|
||||
}
|
||||
// Binding Encryption Keys.
|
||||
// See RFC 4462 section 3.5.
|
||||
micField := buildMIC(string(session), user, "ssh-connection", "gssapi-with-mic")
|
||||
micToken, err := g.gssAPIClient.GetMIC(micField)
|
||||
if err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
if err := c.writePacket(Marshal(&userAuthGSSAPIMIC{
|
||||
MIC: micToken,
|
||||
})); err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
return handleAuthResponse(c)
|
||||
}
|
||||
|
||||
func (g *gssAPIWithMICCallback) method() string {
|
||||
return "gssapi-with-mic"
|
||||
}
|
||||
|
||||
+20
-7
@@ -51,6 +51,13 @@ var supportedKexAlgos = []string{
|
||||
kexAlgoDH14SHA1, kexAlgoDH1SHA1,
|
||||
}
|
||||
|
||||
// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
|
||||
// for the server half.
|
||||
var serverForbiddenKexAlgos = map[string]struct{}{
|
||||
kexAlgoDHGEXSHA1: {}, // server half implementation is only minimal to satisfy the automated tests
|
||||
kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
|
||||
}
|
||||
|
||||
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
|
||||
// of authenticating servers) in preference order.
|
||||
var supportedHostKeyAlgos = []string{
|
||||
@@ -109,6 +116,7 @@ func findCommon(what string, client []string, server []string) (common string, e
|
||||
return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
|
||||
}
|
||||
|
||||
// directionAlgorithms records algorithm choices in one direction (either read or write)
|
||||
type directionAlgorithms struct {
|
||||
Cipher string
|
||||
MAC string
|
||||
@@ -137,7 +145,7 @@ type algorithms struct {
|
||||
r directionAlgorithms
|
||||
}
|
||||
|
||||
func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
|
||||
func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
|
||||
result := &algorithms{}
|
||||
|
||||
result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
|
||||
@@ -150,32 +158,37 @@ func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algor
|
||||
return
|
||||
}
|
||||
|
||||
result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
|
||||
stoc, ctos := &result.w, &result.r
|
||||
if isClient {
|
||||
ctos, stoc = stoc, ctos
|
||||
}
|
||||
|
||||
ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
|
||||
stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
|
||||
ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
|
||||
stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
|
||||
ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
|
||||
stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
+3
-2
@@ -543,7 +543,8 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||
|
||||
clientInit := otherInit
|
||||
serverInit := t.sentInitMsg
|
||||
if len(t.hostKeys) == 0 {
|
||||
isClient := len(t.hostKeys) == 0
|
||||
if isClient {
|
||||
clientInit, serverInit = serverInit, clientInit
|
||||
|
||||
magics.clientKexInit = t.sentInitPacket
|
||||
@@ -551,7 +552,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||
}
|
||||
|
||||
var err error
|
||||
t.algorithms, err = findAgreedAlgorithms(clientInit, serverInit)
|
||||
t.algorithms, err = findAgreedAlgorithms(isClient, clientInit, serverInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+249
@@ -10,7 +10,9 @@ import (
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
@@ -24,6 +26,12 @@ const (
|
||||
kexAlgoECDH384 = "ecdh-sha2-nistp384"
|
||||
kexAlgoECDH521 = "ecdh-sha2-nistp521"
|
||||
kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org"
|
||||
|
||||
// For the following kex only the client half contains a production
|
||||
// ready implementation. The server half only consists of a minimal
|
||||
// implementation to satisfy the automated tests.
|
||||
kexAlgoDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
|
||||
kexAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
|
||||
)
|
||||
|
||||
// kexResult captures the outcome of a key exchange.
|
||||
@@ -402,6 +410,8 @@ func init() {
|
||||
kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
|
||||
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
|
||||
kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
|
||||
kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
|
||||
kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
|
||||
}
|
||||
|
||||
// curve25519sha256 implements the curve25519-sha256@libssh.org key
|
||||
@@ -538,3 +548,242 @@ func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handsh
|
||||
Hash: crypto.SHA256,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// dhGEXSHA implements the diffie-hellman-group-exchange-sha1 and
|
||||
// diffie-hellman-group-exchange-sha256 key agreement protocols,
|
||||
// as described in RFC 4419
|
||||
type dhGEXSHA struct {
|
||||
g, p *big.Int
|
||||
hashFunc crypto.Hash
|
||||
}
|
||||
|
||||
const numMRTests = 64
|
||||
|
||||
const (
|
||||
dhGroupExchangeMinimumBits = 2048
|
||||
dhGroupExchangePreferredBits = 2048
|
||||
dhGroupExchangeMaximumBits = 8192
|
||||
)
|
||||
|
||||
func (gex *dhGEXSHA) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
|
||||
if theirPublic.Sign() <= 0 || theirPublic.Cmp(gex.p) >= 0 {
|
||||
return nil, fmt.Errorf("ssh: DH parameter out of bounds")
|
||||
}
|
||||
return new(big.Int).Exp(theirPublic, myPrivate, gex.p), nil
|
||||
}
|
||||
|
||||
func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
||||
// Send GexRequest
|
||||
kexDHGexRequest := kexDHGexRequestMsg{
|
||||
MinBits: dhGroupExchangeMinimumBits,
|
||||
PreferedBits: dhGroupExchangePreferredBits,
|
||||
MaxBits: dhGroupExchangeMaximumBits,
|
||||
}
|
||||
if err := c.writePacket(Marshal(&kexDHGexRequest)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Receive GexGroup
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kexDHGexGroup kexDHGexGroupMsg
|
||||
if err = Unmarshal(packet, &kexDHGexGroup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// reject if p's bit length < dhGroupExchangeMinimumBits or > dhGroupExchangeMaximumBits
|
||||
if kexDHGexGroup.P.BitLen() < dhGroupExchangeMinimumBits || kexDHGexGroup.P.BitLen() > dhGroupExchangeMaximumBits {
|
||||
return nil, fmt.Errorf("ssh: server-generated gex p is out of range (%d bits)", kexDHGexGroup.P.BitLen())
|
||||
}
|
||||
|
||||
gex.p = kexDHGexGroup.P
|
||||
gex.g = kexDHGexGroup.G
|
||||
|
||||
// Check if p is safe by verifing that p and (p-1)/2 are primes
|
||||
one := big.NewInt(1)
|
||||
var pHalf = &big.Int{}
|
||||
pHalf.Rsh(gex.p, 1)
|
||||
if !gex.p.ProbablyPrime(numMRTests) || !pHalf.ProbablyPrime(numMRTests) {
|
||||
return nil, fmt.Errorf("ssh: server provided gex p is not safe")
|
||||
}
|
||||
|
||||
// Check if g is safe by verifing that g > 1 and g < p - 1
|
||||
var pMinusOne = &big.Int{}
|
||||
pMinusOne.Sub(gex.p, one)
|
||||
if gex.g.Cmp(one) != 1 && gex.g.Cmp(pMinusOne) != -1 {
|
||||
return nil, fmt.Errorf("ssh: server provided gex g is not safe")
|
||||
}
|
||||
|
||||
// Send GexInit
|
||||
x, err := rand.Int(randSource, pHalf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
X := new(big.Int).Exp(gex.g, x, gex.p)
|
||||
kexDHGexInit := kexDHGexInitMsg{
|
||||
X: X,
|
||||
}
|
||||
if err := c.writePacket(Marshal(&kexDHGexInit)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Receive GexReply
|
||||
packet, err = c.readPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kexDHGexReply kexDHGexReplyMsg
|
||||
if err = Unmarshal(packet, &kexDHGexReply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kInt, err := gex.diffieHellman(kexDHGexReply.Y, x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if k is safe by verifing that k > 1 and k < p - 1
|
||||
if kInt.Cmp(one) != 1 && kInt.Cmp(pMinusOne) != -1 {
|
||||
return nil, fmt.Errorf("ssh: derived k is not safe")
|
||||
}
|
||||
|
||||
h := gex.hashFunc.New()
|
||||
magics.write(h)
|
||||
writeString(h, kexDHGexReply.HostKey)
|
||||
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
|
||||
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
|
||||
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
|
||||
writeInt(h, gex.p)
|
||||
writeInt(h, gex.g)
|
||||
writeInt(h, X)
|
||||
writeInt(h, kexDHGexReply.Y)
|
||||
K := make([]byte, intLength(kInt))
|
||||
marshalInt(K, kInt)
|
||||
h.Write(K)
|
||||
|
||||
return &kexResult{
|
||||
H: h.Sum(nil),
|
||||
K: K,
|
||||
HostKey: kexDHGexReply.HostKey,
|
||||
Signature: kexDHGexReply.Signature,
|
||||
Hash: gex.hashFunc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Server half implementation of the Diffie Hellman Key Exchange with SHA1 and SHA256.
|
||||
//
|
||||
// This is a minimal implementation to satisfy the automated tests.
|
||||
func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
|
||||
// Receive GexRequest
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var kexDHGexRequest kexDHGexRequestMsg
|
||||
if err = Unmarshal(packet, &kexDHGexRequest); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// smoosh the user's preferred size into our own limits
|
||||
if kexDHGexRequest.PreferedBits > dhGroupExchangeMaximumBits {
|
||||
kexDHGexRequest.PreferedBits = dhGroupExchangeMaximumBits
|
||||
}
|
||||
if kexDHGexRequest.PreferedBits < dhGroupExchangeMinimumBits {
|
||||
kexDHGexRequest.PreferedBits = dhGroupExchangeMinimumBits
|
||||
}
|
||||
// fix min/max if they're inconsistent. technically, we could just pout
|
||||
// and hang up, but there's no harm in giving them the benefit of the
|
||||
// doubt and just picking a bitsize for them.
|
||||
if kexDHGexRequest.MinBits > kexDHGexRequest.PreferedBits {
|
||||
kexDHGexRequest.MinBits = kexDHGexRequest.PreferedBits
|
||||
}
|
||||
if kexDHGexRequest.MaxBits < kexDHGexRequest.PreferedBits {
|
||||
kexDHGexRequest.MaxBits = kexDHGexRequest.PreferedBits
|
||||
}
|
||||
|
||||
// Send GexGroup
|
||||
// This is the group called diffie-hellman-group14-sha1 in RFC
|
||||
// 4253 and Oakley Group 14 in RFC 3526.
|
||||
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
|
||||
gex.p = p
|
||||
gex.g = big.NewInt(2)
|
||||
|
||||
kexDHGexGroup := kexDHGexGroupMsg{
|
||||
P: gex.p,
|
||||
G: gex.g,
|
||||
}
|
||||
if err := c.writePacket(Marshal(&kexDHGexGroup)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Receive GexInit
|
||||
packet, err = c.readPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var kexDHGexInit kexDHGexInitMsg
|
||||
if err = Unmarshal(packet, &kexDHGexInit); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var pHalf = &big.Int{}
|
||||
pHalf.Rsh(gex.p, 1)
|
||||
|
||||
y, err := rand.Int(randSource, pHalf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
Y := new(big.Int).Exp(gex.g, y, gex.p)
|
||||
kInt, err := gex.diffieHellman(kexDHGexInit.X, y)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostKeyBytes := priv.PublicKey().Marshal()
|
||||
|
||||
h := gex.hashFunc.New()
|
||||
magics.write(h)
|
||||
writeString(h, hostKeyBytes)
|
||||
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
|
||||
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
|
||||
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
|
||||
writeInt(h, gex.p)
|
||||
writeInt(h, gex.g)
|
||||
writeInt(h, kexDHGexInit.X)
|
||||
writeInt(h, Y)
|
||||
|
||||
K := make([]byte, intLength(kInt))
|
||||
marshalInt(K, kInt)
|
||||
h.Write(K)
|
||||
|
||||
H := h.Sum(nil)
|
||||
|
||||
// H is already a hash, but the hostkey signing will apply its
|
||||
// own key-specific hash algorithm.
|
||||
sig, err := signAndMarshal(priv, randSource, H)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kexDHGexReply := kexDHGexReplyMsg{
|
||||
HostKey: hostKeyBytes,
|
||||
Y: Y,
|
||||
Signature: sig,
|
||||
}
|
||||
packet = Marshal(&kexDHGexReply)
|
||||
|
||||
err = c.writePacket(packet)
|
||||
|
||||
return &kexResult{
|
||||
H: H,
|
||||
K: K,
|
||||
HostKey: hostKeyBytes,
|
||||
Signature: sig,
|
||||
Hash: gex.hashFunc,
|
||||
}, err
|
||||
}
|
||||
|
||||
+100
@@ -97,6 +97,36 @@ type kexDHReplyMsg struct {
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
// See RFC 4419, section 5.
|
||||
const msgKexDHGexGroup = 31
|
||||
|
||||
type kexDHGexGroupMsg struct {
|
||||
P *big.Int `sshtype:"31"`
|
||||
G *big.Int
|
||||
}
|
||||
|
||||
const msgKexDHGexInit = 32
|
||||
|
||||
type kexDHGexInitMsg struct {
|
||||
X *big.Int `sshtype:"32"`
|
||||
}
|
||||
|
||||
const msgKexDHGexReply = 33
|
||||
|
||||
type kexDHGexReplyMsg struct {
|
||||
HostKey []byte `sshtype:"33"`
|
||||
Y *big.Int
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
const msgKexDHGexRequest = 34
|
||||
|
||||
type kexDHGexRequestMsg struct {
|
||||
MinBits uint32 `sshtype:"34"`
|
||||
PreferedBits uint32
|
||||
MaxBits uint32
|
||||
}
|
||||
|
||||
// See RFC 4253, section 10.
|
||||
const msgServiceRequest = 5
|
||||
|
||||
@@ -275,6 +305,42 @@ type userAuthPubKeyOkMsg struct {
|
||||
PubKey []byte
|
||||
}
|
||||
|
||||
// See RFC 4462, section 3
|
||||
const msgUserAuthGSSAPIResponse = 60
|
||||
|
||||
type userAuthGSSAPIResponse struct {
|
||||
SupportMech []byte `sshtype:"60"`
|
||||
}
|
||||
|
||||
const msgUserAuthGSSAPIToken = 61
|
||||
|
||||
type userAuthGSSAPIToken struct {
|
||||
Token []byte `sshtype:"61"`
|
||||
}
|
||||
|
||||
const msgUserAuthGSSAPIMIC = 66
|
||||
|
||||
type userAuthGSSAPIMIC struct {
|
||||
MIC []byte `sshtype:"66"`
|
||||
}
|
||||
|
||||
// See RFC 4462, section 3.9
|
||||
const msgUserAuthGSSAPIErrTok = 64
|
||||
|
||||
type userAuthGSSAPIErrTok struct {
|
||||
ErrorToken []byte `sshtype:"64"`
|
||||
}
|
||||
|
||||
// See RFC 4462, section 3.8
|
||||
const msgUserAuthGSSAPIError = 65
|
||||
|
||||
type userAuthGSSAPIError struct {
|
||||
MajorStatus uint32 `sshtype:"65"`
|
||||
MinorStatus uint32
|
||||
Message string
|
||||
LanguageTag string
|
||||
}
|
||||
|
||||
// typeTags returns the possible type bytes for the given reflect.Type, which
|
||||
// should be a struct. The possible values are separated by a '|' character.
|
||||
func typeTags(structType reflect.Type) (tags []byte) {
|
||||
@@ -756,6 +822,14 @@ func decode(packet []byte) (interface{}, error) {
|
||||
msg = new(channelRequestSuccessMsg)
|
||||
case msgChannelFailure:
|
||||
msg = new(channelRequestFailureMsg)
|
||||
case msgUserAuthGSSAPIToken:
|
||||
msg = new(userAuthGSSAPIToken)
|
||||
case msgUserAuthGSSAPIMIC:
|
||||
msg = new(userAuthGSSAPIMIC)
|
||||
case msgUserAuthGSSAPIErrTok:
|
||||
msg = new(userAuthGSSAPIErrTok)
|
||||
case msgUserAuthGSSAPIError:
|
||||
msg = new(userAuthGSSAPIError)
|
||||
default:
|
||||
return nil, unexpectedMessageError(0, packet[0])
|
||||
}
|
||||
@@ -764,3 +838,29 @@ func decode(packet []byte) (interface{}, error) {
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
var packetTypeNames = map[byte]string{
|
||||
msgDisconnect: "disconnectMsg",
|
||||
msgServiceRequest: "serviceRequestMsg",
|
||||
msgServiceAccept: "serviceAcceptMsg",
|
||||
msgKexInit: "kexInitMsg",
|
||||
msgKexDHInit: "kexDHInitMsg",
|
||||
msgKexDHReply: "kexDHReplyMsg",
|
||||
msgUserAuthRequest: "userAuthRequestMsg",
|
||||
msgUserAuthSuccess: "userAuthSuccessMsg",
|
||||
msgUserAuthFailure: "userAuthFailureMsg",
|
||||
msgUserAuthPubKeyOk: "userAuthPubKeyOkMsg",
|
||||
msgGlobalRequest: "globalRequestMsg",
|
||||
msgRequestSuccess: "globalRequestSuccessMsg",
|
||||
msgRequestFailure: "globalRequestFailureMsg",
|
||||
msgChannelOpen: "channelOpenMsg",
|
||||
msgChannelData: "channelDataMsg",
|
||||
msgChannelOpenConfirm: "channelOpenConfirmMsg",
|
||||
msgChannelOpenFailure: "channelOpenFailureMsg",
|
||||
msgChannelWindowAdjust: "windowAdjustMsg",
|
||||
msgChannelEOF: "channelEOFMsg",
|
||||
msgChannelClose: "channelCloseMsg",
|
||||
msgChannelRequest: "channelRequestMsg",
|
||||
msgChannelSuccess: "channelRequestSuccessMsg",
|
||||
msgChannelFailure: "channelRequestFailureMsg",
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user