feat(t226): add Go ERP session login
This commit is contained in:
@@ -21,6 +21,9 @@ const (
|
||||
ReadinessTTLEnvironment = "CMROUBAO_READINESS_TTL"
|
||||
ERPConnectorURLEnvironment = "CMROUBAO_ERP_CONNECTOR_URL"
|
||||
ERPConnectorAPIKeyEnvironment = "CMROUBAO_ERP_CONNECTOR_API_KEY"
|
||||
ShunyunbaoURLEnvironment = "CMROUBAO_SHUNYUNBAO_URL"
|
||||
ShunyunbaoUsernameEnvironment = "CMROUBAO_SHUNYUNBAO_USERNAME"
|
||||
ShunyunbaoPasswordEnvironment = "CMROUBAO_SHUNYUNBAO_PASSWORD"
|
||||
|
||||
defaultHTTPAddress = "127.0.0.1:8080"
|
||||
defaultDatabasePath = "var/cmroubao.db"
|
||||
@@ -29,6 +32,7 @@ const (
|
||||
defaultRunningLease = 30 * time.Minute
|
||||
defaultReadinessTTL = 2 * time.Minute
|
||||
defaultERPConnectorURL = "http://127.0.0.1:8091"
|
||||
defaultShunyunbaoURL = "https://www.shunyunbaoerp.com"
|
||||
)
|
||||
|
||||
type LookupEnvironment func(string) (string, bool)
|
||||
@@ -51,6 +55,10 @@ type Config struct {
|
||||
ERPConnectorURL string
|
||||
ERPConnectorAPIKey string
|
||||
ERPConnectorTimeout time.Duration
|
||||
ShunyunbaoURL string
|
||||
ShunyunbaoUsername string
|
||||
ShunyunbaoPassword string
|
||||
ShunyunbaoTimeout time.Duration
|
||||
}
|
||||
|
||||
func Load(lookup LookupEnvironment) (Config, error) {
|
||||
@@ -165,6 +173,37 @@ func Load(lookup LookupEnvironment) (Config, error) {
|
||||
)
|
||||
}
|
||||
}
|
||||
shunyunbaoURL, err := environmentValue(
|
||||
lookup,
|
||||
ShunyunbaoURLEnvironment,
|
||||
defaultShunyunbaoURL,
|
||||
)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
if err := validateHTTPSOrigin(shunyunbaoURL, ShunyunbaoURLEnvironment); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
shunyunbaoUsername, usernameSet, err := optionalEnvironmentValue(
|
||||
lookup,
|
||||
ShunyunbaoUsernameEnvironment,
|
||||
)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
shunyunbaoPassword, passwordSet, err := optionalSecretEnvironmentValue(
|
||||
lookup,
|
||||
ShunyunbaoPasswordEnvironment,
|
||||
)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
if usernameSet != passwordSet {
|
||||
return Config{}, errors.New(
|
||||
ShunyunbaoUsernameEnvironment + " and " +
|
||||
ShunyunbaoPasswordEnvironment + " must be set together",
|
||||
)
|
||||
}
|
||||
|
||||
return Config{
|
||||
HTTPAddress: httpAddress,
|
||||
@@ -184,6 +223,10 @@ func Load(lookup LookupEnvironment) (Config, error) {
|
||||
ERPConnectorURL: strings.TrimRight(erpConnectorURL, "/"),
|
||||
ERPConnectorAPIKey: erpConnectorAPIKey,
|
||||
ERPConnectorTimeout: 90 * time.Second,
|
||||
ShunyunbaoURL: strings.TrimRight(shunyunbaoURL, "/"),
|
||||
ShunyunbaoUsername: shunyunbaoUsername,
|
||||
ShunyunbaoPassword: shunyunbaoPassword,
|
||||
ShunyunbaoTimeout: 30 * time.Second,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -214,6 +257,18 @@ func validateLoopbackURL(value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateHTTPSOrigin(value, environment string) error {
|
||||
parsed, err := url.Parse(value)
|
||||
if err != nil || parsed.Scheme != "https" || parsed.Host == "" ||
|
||||
parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" ||
|
||||
(parsed.Path != "" && parsed.Path != "/") {
|
||||
return errors.New(
|
||||
environment + " must be an https origin without credentials or path",
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func durationEnvironment(
|
||||
lookup LookupEnvironment,
|
||||
name string,
|
||||
@@ -261,6 +316,20 @@ func optionalEnvironmentValue(
|
||||
return value, true, nil
|
||||
}
|
||||
|
||||
func optionalSecretEnvironmentValue(
|
||||
lookup LookupEnvironment,
|
||||
name string,
|
||||
) (string, bool, error) {
|
||||
value, exists := lookup(name)
|
||||
if !exists {
|
||||
return "", false, nil
|
||||
}
|
||||
if value == "" || strings.ContainsRune(value, '\x00') {
|
||||
return "", false, errors.New(name + " must not be blank")
|
||||
}
|
||||
return value, true, nil
|
||||
}
|
||||
|
||||
func isLoopbackAddress(address string) bool {
|
||||
host, _, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
|
||||
@@ -52,6 +52,17 @@ func TestLoadUsesSafeDefaults(t *testing.T) {
|
||||
cfg.ERPConnectorTimeout,
|
||||
)
|
||||
}
|
||||
if cfg.ShunyunbaoURL != "https://www.shunyunbaoerp.com" ||
|
||||
cfg.ShunyunbaoUsername != "" || cfg.ShunyunbaoPassword != "" ||
|
||||
cfg.ShunyunbaoTimeout != 30*time.Second {
|
||||
t.Fatalf(
|
||||
"shunyunbao defaults = %q / %q / %q / %s",
|
||||
cfg.ShunyunbaoURL,
|
||||
cfg.ShunyunbaoUsername,
|
||||
cfg.ShunyunbaoPassword,
|
||||
cfg.ShunyunbaoTimeout,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadAcceptsExplicitConfiguration(t *testing.T) {
|
||||
@@ -66,6 +77,9 @@ func TestLoadAcceptsExplicitConfiguration(t *testing.T) {
|
||||
ReadinessTTLEnvironment: "3m",
|
||||
ERPConnectorURLEnvironment: "http://localhost:18091",
|
||||
ERPConnectorAPIKeyEnvironment: "12345678901234567890123456789012",
|
||||
ShunyunbaoURLEnvironment: "https://erp.example.test:8443",
|
||||
ShunyunbaoUsernameEnvironment: "service-user",
|
||||
ShunyunbaoPasswordEnvironment: " pass with spaces ",
|
||||
}
|
||||
|
||||
cfg, err := Load(mapEnvironment(values))
|
||||
@@ -108,6 +122,11 @@ func TestLoadAcceptsExplicitConfiguration(t *testing.T) {
|
||||
cfg.ERPConnectorAPIKey,
|
||||
)
|
||||
}
|
||||
if cfg.ShunyunbaoURL != values[ShunyunbaoURLEnvironment] ||
|
||||
cfg.ShunyunbaoUsername != values[ShunyunbaoUsernameEnvironment] ||
|
||||
cfg.ShunyunbaoPassword != values[ShunyunbaoPasswordEnvironment] {
|
||||
t.Fatalf("shunyunbao config was not preserved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRejectsUnsafeOrInvalidValues(t *testing.T) {
|
||||
@@ -133,6 +152,30 @@ func TestLoadRejectsUnsafeOrInvalidValues(t *testing.T) {
|
||||
ERPConnectorAPIKeyEnvironment: "short",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non HTTPS shunyunbao URL",
|
||||
values: map[string]string{
|
||||
ShunyunbaoURLEnvironment: "http://erp.example.test",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "shunyunbao URL path",
|
||||
values: map[string]string{
|
||||
ShunyunbaoURLEnvironment: "https://erp.example.test/private",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "shunyunbao username without password",
|
||||
values: map[string]string{
|
||||
ShunyunbaoUsernameEnvironment: "service-user",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "shunyunbao password without username",
|
||||
values: map[string]string{
|
||||
ShunyunbaoPasswordEnvironment: "password",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "blank explicit address",
|
||||
values: map[string]string{
|
||||
|
||||
Reference in New Issue
Block a user