15107: Add Users.PreferDomainForUsername config.
authorTom Clegg <tclegg@veritasgenetics.com>
Fri, 15 Nov 2019 04:32:40 +0000 (23:32 -0500)
committerTom Clegg <tclegg@veritasgenetics.com>
Fri, 15 Nov 2019 04:32:40 +0000 (23:32 -0500)
Corresponds to get_username_from_domain feature in sso-provider.

Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg@veritasgenetics.com>

lib/config/config.default.yml
lib/config/export.go
lib/config/generated_config.go
lib/controller/localdb/login.go
lib/controller/localdb/login_test.go
lib/controller/rpc/conn.go
sdk/go/arvados/config.go
services/api/app/models/user.rb

index 81c36b9bfbe2b00626403cfde5cbdfd2894c34f9..6afbf429cfa928312228780ba9f1aaeff17f97e9 100644 (file)
@@ -275,6 +275,12 @@ Clusters:
       # in the directory where your API server is running.
       AnonymousUserToken: ""
 
+      # If a new user has an alternate email address (local@domain)
+      # with the domain given here, its local part becomes the new
+      # user's default username. Otherwise, the user's primary email
+      # address is used.
+      PreferDomainForUsername: ""
+
     AuditLogs:
       # Time to keep audit logs, in seconds. (An audit log is a row added
       # to the "logs" table in the PostgreSQL database each time an
index 7adacab4c8df392ef35f57c6ecb6d1cbe8f7d749..413ff9578c3af7ca79e5cf20add2a0dc563c10d1 100644 (file)
@@ -167,6 +167,7 @@ var whitelist = map[string]bool{
        "Users.NewInactiveUserNotificationRecipients":  false,
        "Users.NewUserNotificationRecipients":          false,
        "Users.NewUsersAreActive":                      false,
+       "Users.PreferDomainForUsername":                false,
        "Users.UserNotifierEmailFrom":                  false,
        "Users.UserProfileNotificationAddress":         false,
        "Volumes":                                      true,
index 68dea169f843072aa33c4f6905e6651011c57fe4..43f29a2c1039ecb54ee492f7fd26e197b3fad7a8 100644 (file)
@@ -281,6 +281,12 @@ Clusters:
       # in the directory where your API server is running.
       AnonymousUserToken: ""
 
+      # If a new user has an alternate email address (local@domain)
+      # with the domain given here, its local part becomes the new
+      # user's default username. Otherwise, the user's primary email
+      # address is used.
+      PreferDomainForUsername: ""
+
     AuditLogs:
       # Time to keep audit logs, in seconds. (An audit log is a row added
       # to the "logs" table in the PostgreSQL database each time an
index 13ae366eb434bded6dd1a4dc034a7c843a5fe86d..dc634e8d8f6fdba47989ce81b8b3bee59741bcd9 100644 (file)
@@ -207,6 +207,9 @@ func (ctrl *googleLoginController) getAuthInfo(ctx context.Context, cluster *arv
        for ae := range altEmails {
                if ae != ret.Email {
                        ret.AlternateEmails = append(ret.AlternateEmails, ae)
+                       if i := strings.Index(ae, "@"); i > 0 && strings.ToLower(ae[i+1:]) == strings.ToLower(cluster.Users.PreferDomainForUsername) {
+                               ret.Username = strings.SplitN(ae[:i], "+", 2)[0]
+                       }
                }
        }
        return &ret, nil
index c5b9ee06832cd663b10893ed3858a3a901437370..3cc4c380c1186b7c11fceb3a0e82bdaf991cae2a 100644 (file)
@@ -148,6 +148,7 @@ func (s *LoginSuite) SetUpTest(c *check.C) {
        s.cluster, err = cfg.GetCluster("")
        s.cluster.Login.GoogleClientID = "test%client$id"
        s.cluster.Login.GoogleClientSecret = "test#client/secret"
+       s.cluster.Users.PreferDomainForUsername = "PreferDomainForUsername.example.com"
        c.Assert(err, check.IsNil)
 
        s.localdb = NewConn(s.cluster)
@@ -364,6 +365,10 @@ func (s *LoginSuite) TestGoogleLogin_AlternateEmailAddresses_Primary(c *check.C)
                                "metadata": map[string]interface{}{"verified": true},
                                "value":    "joe.smith@alternate.example.com",
                        },
+                       {
+                               "metadata": map[string]interface{}{"verified": true},
+                               "value":    "joe.smith@preferdomainforusername.example.com",
+                       },
                },
        }
        state := s.startLogin(c)
@@ -373,7 +378,8 @@ func (s *LoginSuite) TestGoogleLogin_AlternateEmailAddresses_Primary(c *check.C)
        })
        authinfo := s.getCallbackAuthInfo(c)
        c.Check(authinfo.Email, check.Equals, "joe.smith@primary.example.com")
-       c.Check(authinfo.AlternateEmails, check.DeepEquals, []string{"joe.smith@alternate.example.com"})
+       c.Check(authinfo.AlternateEmails, check.DeepEquals, []string{"joe.smith@alternate.example.com", "joe.smith@preferdomainforusername.example.com"})
+       c.Check(authinfo.Username, check.Equals, "joe.smith")
 }
 
 func (s *LoginSuite) TestGoogleLogin_NoPrimaryEmailAddress(c *check.C) {
@@ -400,6 +406,7 @@ func (s *LoginSuite) TestGoogleLogin_NoPrimaryEmailAddress(c *check.C) {
        authinfo := s.getCallbackAuthInfo(c)
        c.Check(authinfo.Email, check.Equals, "joe.smith@work.example.com") // first verified email in People response
        c.Check(authinfo.AlternateEmails, check.DeepEquals, []string{"joe.smith@home.example.com"})
+       c.Check(authinfo.Username, check.Equals, "")
 }
 
 func (s *LoginSuite) getCallbackAuthInfo(c *check.C) (authinfo rpc.UserSessionAuthInfo) {
index 7d7cb486f4f742d57411751254cf7b8dd6ab22ad..3d6a9852089c3005a084e61290da0b45f0a67489 100644 (file)
@@ -320,6 +320,7 @@ type UserSessionAuthInfo struct {
        AlternateEmails []string `json:"alternate_emails"`
        FirstName       string   `json:"first_name"`
        LastName        string   `json:"last_name"`
+       Username        string   `json:"username"`
 }
 
 type UserSessionCreateOptions struct {
index 805efb7db287d61a9049cf0abf69ac61236989d4..72128a9dcd385d5d7274567bde7c836417af09f3 100644 (file)
@@ -174,6 +174,7 @@ type Cluster struct {
                NewUsersAreActive                     bool
                UserNotifierEmailFrom                 string
                UserProfileNotificationAddress        string
+               PreferDomainForUsername               string
        }
        Volumes   map[string]Volume
        Workbench struct {
index 7a3a854b3a17826117a6ff913ff5f20743f86483..a49aa6f56a22ead2398198401c15be2a8860ec97 100644 (file)
@@ -435,7 +435,7 @@ class User < ArvadosModel
                               :is_admin => false,
                               :is_active => Rails.configuration.Users.NewUsersAreActive)
 
-      primary_user.set_initial_username(requested: info['username']) if info['username']
+      primary_user.set_initial_username(requested: info['username']) if info['username'] && !info['username'].blank?
       primary_user.identity_url = info['identity_url'] if identity_url
     end