[ARVADOS] created: 1.3.0-2905-g20cea53c4

Git user git at public.arvados.org
Tue Aug 18 20:44:02 UTC 2020


        at  20cea53c48260a0cec3d588c2af520b27433b8c1 (commit)


commit 20cea53c48260a0cec3d588c2af520b27433b8c1
Author: Tom Clegg <tom at tomclegg.ca>
Date:   Tue Aug 18 16:37:58 2020 -0400

    16314: Support "Test" authentication type.
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at tomclegg.ca>

diff --git a/go.sum b/go.sum
index 2565964e7..ac5c03fc8 100644
--- a/go.sum
+++ b/go.sum
@@ -78,6 +78,7 @@ github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHj
 github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
@@ -226,6 +227,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowK
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
diff --git a/lib/config/config.default.yml b/lib/config/config.default.yml
index 270d4045b..cac0ac61d 100644
--- a/lib/config/config.default.yml
+++ b/lib/config/config.default.yml
@@ -686,6 +686,16 @@ Clusters:
         ProviderAppID: ""
         ProviderAppSecret: ""
 
+      Test:
+        # Authenticate users listed here in the config file. This
+        # feature is intended to be used in test environments, and
+        # should not be used in production.
+        Enable: false
+        Users:
+          SAMPLE:
+            email: alice at example.com
+            password: xyzzy
+
       # The cluster ID to delegate the user database.  When set,
       # logins on this cluster will be redirected to the login cluster
       # (login cluster must appear in RemoteClusters with Proxy: true)
diff --git a/lib/config/generated_config.go b/lib/config/generated_config.go
index 0241673aa..e3bd13fd2 100644
--- a/lib/config/generated_config.go
+++ b/lib/config/generated_config.go
@@ -692,6 +692,16 @@ Clusters:
         ProviderAppID: ""
         ProviderAppSecret: ""
 
+      Test:
+        # Authenticate users listed here in the config file. This
+        # feature is intended to be used in test environments, and
+        # should not be used in production.
+        Enable: false
+        Users:
+          SAMPLE:
+            email: alice at example.com
+            password: xyzzy
+
       # The cluster ID to delegate the user database.  When set,
       # logins on this cluster will be redirected to the login cluster
       # (login cluster must appear in RemoteClusters with Proxy: true)
diff --git a/lib/controller/localdb/login.go b/lib/controller/localdb/login.go
index ee1ea5692..34da4933a 100644
--- a/lib/controller/localdb/login.go
+++ b/lib/controller/localdb/login.go
@@ -33,8 +33,9 @@ func chooseLoginController(cluster *arvados.Cluster, railsProxy *railsProxy) log
 	wantSSO := cluster.Login.SSO.Enable
 	wantPAM := cluster.Login.PAM.Enable
 	wantLDAP := cluster.Login.LDAP.Enable
+	wantTest := cluster.Login.Test.Enable
 	switch {
-	case wantGoogle && !wantOpenIDConnect && !wantSSO && !wantPAM && !wantLDAP:
+	case wantGoogle && !wantOpenIDConnect && !wantSSO && !wantPAM && !wantLDAP && !wantTest:
 		return &oidcLoginController{
 			Cluster:            cluster,
 			RailsProxy:         railsProxy,
@@ -45,7 +46,7 @@ func chooseLoginController(cluster *arvados.Cluster, railsProxy *railsProxy) log
 			EmailClaim:         "email",
 			EmailVerifiedClaim: "email_verified",
 		}
-	case !wantGoogle && wantOpenIDConnect && !wantSSO && !wantPAM && !wantLDAP:
+	case !wantGoogle && wantOpenIDConnect && !wantSSO && !wantPAM && !wantLDAP && !wantTest:
 		return &oidcLoginController{
 			Cluster:            cluster,
 			RailsProxy:         railsProxy,
@@ -56,15 +57,17 @@ func chooseLoginController(cluster *arvados.Cluster, railsProxy *railsProxy) log
 			EmailVerifiedClaim: cluster.Login.OpenIDConnect.EmailVerifiedClaim,
 			UsernameClaim:      cluster.Login.OpenIDConnect.UsernameClaim,
 		}
-	case !wantGoogle && !wantOpenIDConnect && wantSSO && !wantPAM && !wantLDAP:
+	case !wantGoogle && !wantOpenIDConnect && wantSSO && !wantPAM && !wantLDAP && !wantTest:
 		return &ssoLoginController{railsProxy}
-	case !wantGoogle && !wantOpenIDConnect && !wantSSO && wantPAM && !wantLDAP:
+	case !wantGoogle && !wantOpenIDConnect && !wantSSO && wantPAM && !wantLDAP && !wantTest:
 		return &pamLoginController{Cluster: cluster, RailsProxy: railsProxy}
-	case !wantGoogle && !wantOpenIDConnect && !wantSSO && !wantPAM && wantLDAP:
+	case !wantGoogle && !wantOpenIDConnect && !wantSSO && !wantPAM && wantLDAP && !wantTest:
 		return &ldapLoginController{Cluster: cluster, RailsProxy: railsProxy}
+	case !wantGoogle && !wantOpenIDConnect && !wantSSO && !wantPAM && !wantLDAP && wantTest:
+		return &testLoginController{Cluster: cluster, RailsProxy: railsProxy}
 	default:
 		return errorLoginController{
-			error: errors.New("configuration problem: exactly one of Login.Google, Login.OpenIDConnect, Login.SSO, Login.PAM, and Login.LDAP must be enabled"),
+			error: errors.New("configuration problem: exactly one of Login.Google, Login.OpenIDConnect, Login.SSO, Login.PAM, Login.LDAP, and Login.Test must be enabled"),
 		}
 	}
 }
diff --git a/lib/controller/localdb/login_testuser.go b/lib/controller/localdb/login_testuser.go
new file mode 100644
index 000000000..5a3d803b8
--- /dev/null
+++ b/lib/controller/localdb/login_testuser.go
@@ -0,0 +1,45 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package localdb
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	"git.arvados.org/arvados.git/lib/controller/rpc"
+	"git.arvados.org/arvados.git/sdk/go/arvados"
+	"git.arvados.org/arvados.git/sdk/go/ctxlog"
+	"github.com/sirupsen/logrus"
+)
+
+type testLoginController struct {
+	Cluster    *arvados.Cluster
+	RailsProxy *railsProxy
+}
+
+func (ctrl *testLoginController) Logout(ctx context.Context, opts arvados.LogoutOptions) (arvados.LogoutResponse, error) {
+	return noopLogout(ctrl.Cluster, opts)
+}
+
+func (ctrl *testLoginController) Login(ctx context.Context, opts arvados.LoginOptions) (arvados.LoginResponse, error) {
+	return arvados.LoginResponse{}, errors.New("interactive login is not available")
+}
+
+func (ctrl *testLoginController) UserAuthenticate(ctx context.Context, opts arvados.UserAuthenticateOptions) (arvados.APIClientAuthorization, error) {
+	for username, user := range ctrl.Cluster.Login.Test.Users {
+		if (opts.Username == username || opts.Username == user.Email) && opts.Password == user.Password {
+			ctxlog.FromContext(ctx).WithFields(logrus.Fields{
+				"username": username,
+				"email":    user.Email,
+			}).Debug("test authentication succeeded")
+			return createAPIClientAuthorization(ctx, ctrl.RailsProxy, ctrl.Cluster.SystemRootToken, rpc.UserSessionAuthInfo{
+				Username: username,
+				Email:    user.Email,
+			})
+		}
+	}
+	return arvados.APIClientAuthorization{}, fmt.Errorf("authentication failed for user %q with password len=%d", opts.Username, len(opts.Password))
+}
diff --git a/lib/controller/localdb/login_testuser_test.go b/lib/controller/localdb/login_testuser_test.go
new file mode 100644
index 000000000..d2d651e20
--- /dev/null
+++ b/lib/controller/localdb/login_testuser_test.go
@@ -0,0 +1,94 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package localdb
+
+import (
+	"context"
+
+	"git.arvados.org/arvados.git/lib/config"
+	"git.arvados.org/arvados.git/lib/controller/rpc"
+	"git.arvados.org/arvados.git/lib/ctrlctx"
+	"git.arvados.org/arvados.git/sdk/go/arvados"
+	"git.arvados.org/arvados.git/sdk/go/arvadostest"
+	"git.arvados.org/arvados.git/sdk/go/ctxlog"
+	"github.com/jmoiron/sqlx"
+	check "gopkg.in/check.v1"
+)
+
+var _ = check.Suite(&TestUserSuite{})
+
+type TestUserSuite struct {
+	cluster  *arvados.Cluster
+	ctrl     *testLoginController
+	railsSpy *arvadostest.Proxy
+	db       *sqlx.DB
+
+	// transaction context
+	ctx      context.Context
+	rollback func() error
+}
+
+func (s *TestUserSuite) SetUpSuite(c *check.C) {
+	cfg, err := config.NewLoader(nil, ctxlog.TestLogger(c)).Load()
+	c.Assert(err, check.IsNil)
+	s.cluster, err = cfg.GetCluster("")
+	c.Assert(err, check.IsNil)
+	s.cluster.Login.Test.Enable = true
+	s.cluster.Login.Test.Users = map[string]arvados.TestUser{
+		"valid": {Email: "valid at example.com", Password: "v at l1d"},
+	}
+	s.railsSpy = arvadostest.NewProxy(c, s.cluster.Services.RailsAPI)
+	s.ctrl = &testLoginController{
+		Cluster:    s.cluster,
+		RailsProxy: rpc.NewConn(s.cluster.ClusterID, s.railsSpy.URL, true, rpc.PassthroughTokenProvider),
+	}
+	s.db = arvadostest.DB(c, s.cluster)
+}
+
+func (s *TestUserSuite) SetUpTest(c *check.C) {
+	tx, err := s.db.Beginx()
+	c.Assert(err, check.IsNil)
+	s.ctx = ctrlctx.NewWithTransaction(context.Background(), tx)
+	s.rollback = tx.Rollback
+}
+
+func (s *TestUserSuite) TearDownTest(c *check.C) {
+	if s.rollback != nil {
+		s.rollback()
+	}
+}
+
+func (s *TestUserSuite) TestLogin(c *check.C) {
+	for _, trial := range []struct {
+		success  bool
+		username string
+		password string
+	}{
+		{false, "foo", "bar"},
+		{false, "", ""},
+		{false, "valid", ""},
+		{false, "", "v at l1d"},
+		{true, "valid", "v at l1d"},
+		{true, "valid at example.com", "v at l1d"},
+	} {
+		c.Logf("=== %#v", trial)
+		resp, err := s.ctrl.UserAuthenticate(s.ctx, arvados.UserAuthenticateOptions{
+			Username: trial.username,
+			Password: trial.password,
+		})
+		if trial.success {
+			c.Check(err, check.IsNil)
+			c.Check(resp.APIToken, check.Not(check.Equals), "")
+			c.Check(resp.UUID, check.Matches, `zzzzz-gj3su-.*`)
+			c.Check(resp.Scopes, check.DeepEquals, []string{"all"})
+
+			authinfo := getCallbackAuthInfo(c, s.railsSpy)
+			c.Check(authinfo.Email, check.Equals, "valid at example.com")
+			c.Check(authinfo.AlternateEmails, check.DeepEquals, []string(nil))
+		} else {
+			c.Check(err, check.ErrorMatches, `authentication failed.*`)
+		}
+	}
+}
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index c21addbba..e123671d4 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -176,6 +176,10 @@ type Cluster struct {
 			ProviderAppID     string
 			ProviderAppSecret string
 		}
+		Test struct {
+			Enable bool
+			Users  map[string]TestUser
+		}
 		LoginCluster       string
 		RemoteTokenRefresh Duration
 	}
@@ -329,6 +333,11 @@ type Service struct {
 	ExternalURL  URL
 }
 
+type TestUser struct {
+	Email    string
+	Password string
+}
+
 // URL is a url.URL that is also usable as a JSON key/value.
 type URL url.URL
 

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list