diff --git a/connector/authproxy/authproxy.go b/connector/authproxy/authproxy.go index 8715412146..7b089818b7 100644 --- a/connector/authproxy/authproxy.go +++ b/connector/authproxy/authproxy.go @@ -19,33 +19,53 @@ import ( // Headers retrieved to fetch user's email and group can be configured // with userHeader and groupHeader. type Config struct { - UserHeader string `json:"userHeader"` - GroupHeader string `json:"groupHeader"` - Groups []string `json:"staticGroups"` + UserIDHeader string `json:"userIDHeader"` + UserHeader string `json:"userHeader"` + EmailHeader string `json:"emailHeader"` + GroupHeader string `json:"groupHeader"` + Groups []string `json:"staticGroups"` } // Open returns an authentication strategy which requires no user interaction. func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) { + userIDHeader := c.UserIDHeader + if userIDHeader == "" { + userIDHeader = "X-Remote-User-Id" + } userHeader := c.UserHeader if userHeader == "" { userHeader = "X-Remote-User" } + emailHeader := c.EmailHeader + if emailHeader == "" { + emailHeader = "X-Remote-User-Email" + } groupHeader := c.GroupHeader if groupHeader == "" { groupHeader = "X-Remote-Group" } - return &callback{userHeader: userHeader, groupHeader: groupHeader, logger: logger, pathSuffix: "/" + id, groups: c.Groups}, nil + return &callback{ + userIDHeader: userIDHeader, + userHeader: userHeader, + emailHeader: emailHeader, + groupHeader: groupHeader, + groups: c.Groups, + logger: logger, + pathSuffix: "/" + id, + }, nil } // Callback is a connector which returns an identity with the HTTP header // X-Remote-User as verified email. type callback struct { - userHeader string - groupHeader string - groups []string - logger log.Logger - pathSuffix string + userIDHeader string + userHeader string + emailHeader string + groupHeader string + groups []string + logger log.Logger + pathSuffix string } // LoginURL returns the URL to redirect the user to login with. @@ -67,6 +87,14 @@ func (m *callback) HandleCallback(s connector.Scopes, r *http.Request) (connecto if remoteUser == "" { return connector.Identity{}, fmt.Errorf("required HTTP header %s is not set", m.userHeader) } + remoteUserID := r.Header.Get(m.userIDHeader) + if remoteUserID == "" { + remoteUserID = remoteUser + } + remoteUserEmail := r.Header.Get(m.emailHeader) + if remoteUserEmail == "" { + remoteUserEmail = remoteUser + } groups := m.groups headerGroup := r.Header.Get(m.groupHeader) if headerGroup != "" { @@ -77,9 +105,10 @@ func (m *callback) HandleCallback(s connector.Scopes, r *http.Request) (connecto groups = append(splitheaderGroup, groups...) } return connector.Identity{ - UserID: remoteUser, // TODO: figure out if this is a bad ID value. - Email: remoteUser, - EmailVerified: true, - Groups: groups, + UserID: remoteUserID, + PreferredUsername: remoteUser, + Email: remoteUserEmail, + EmailVerified: true, + Groups: groups, }, nil } diff --git a/connector/authproxy/authproxy_test.go b/connector/authproxy/authproxy_test.go index 5d42530e07..1f234d9a8d 100644 --- a/connector/authproxy/authproxy_test.go +++ b/connector/authproxy/authproxy_test.go @@ -19,6 +19,8 @@ const ( testGroup4 = "group 4" testStaticGroup1 = "static1" testStaticGroup2 = "static 2" + testUsername = "testuser" + testUserID = "1234567890" ) var logger = &logrus.Logger{Out: io.Discard, Formatter: &logrus.TextFormatter{}} @@ -32,13 +34,46 @@ func TestUser(t *testing.T) { req, err := http.NewRequest("GET", "/", nil) expectNil(t, err) req.Header = map[string][]string{ - "X-Remote-User": {testEmail}, + "X-Remote-User": {testUsername}, } ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req) expectNil(t, err) - expectEquals(t, ident.UserID, testEmail) + // If not specified, the userID and email should fall back to the remote user + expectEquals(t, ident.UserID, testUsername) + expectEquals(t, ident.PreferredUsername, testUsername) + expectEquals(t, ident.Email, testUsername) + expectEquals(t, len(ident.Groups), 0) +} + +func TestExtraHeaders(t *testing.T) { + config := Config{ + UserIDHeader: "X-Remote-User-Id", + UserHeader: "X-Remote-User", + EmailHeader: "X-Remote-User-Email", + } + conn := callback{ + userHeader: config.UserHeader, + userIDHeader: config.UserIDHeader, + emailHeader: config.EmailHeader, + logger: logger, + pathSuffix: "/test", + } + + req, err := http.NewRequest("GET", "/", nil) + expectNil(t, err) + req.Header = map[string][]string{ + "X-Remote-User-Id": {testUserID}, + "X-Remote-User": {testUsername}, + "X-Remote-User-Email": {testEmail}, + } + + ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req) + expectNil(t, err) + + expectEquals(t, ident.UserID, testUserID) + expectEquals(t, ident.PreferredUsername, testUsername) expectEquals(t, ident.Email, testEmail) expectEquals(t, len(ident.Groups), 0) }