*: connectors use a different identity object than storage
This commit is contained in:
		| @@ -1,11 +1,7 @@ | ||||
| // Package connector defines interfaces for federated identity strategies. | ||||
| package connector | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/coreos/poke/storage" | ||||
| ) | ||||
| import "net/http" | ||||
|  | ||||
| // Connector is a mechanism for federating login to a remote identity service. | ||||
| // | ||||
| @@ -15,18 +11,32 @@ type Connector interface { | ||||
| 	Close() error | ||||
| } | ||||
|  | ||||
| // Identity represents the ID Token claims supported by the server. | ||||
| type Identity struct { | ||||
| 	UserID        string | ||||
| 	Username      string | ||||
| 	Email         string | ||||
| 	EmailVerified bool | ||||
|  | ||||
| 	// ConnectorData holds data used by the connector for subsequent requests after initial | ||||
| 	// authentication, such as access tokens for upstream provides. | ||||
| 	// | ||||
| 	// This data is never shared with end users, OAuth clients, or through the API. | ||||
| 	ConnectorData []byte | ||||
| } | ||||
|  | ||||
| // PasswordConnector is an optional interface for password based connectors. | ||||
| type PasswordConnector interface { | ||||
| 	Login(username, password string) (identity storage.Identity, validPassword bool, err error) | ||||
| 	Login(username, password string) (identity Identity, validPassword bool, err error) | ||||
| } | ||||
|  | ||||
| // CallbackConnector is an optional interface for callback based connectors. | ||||
| type CallbackConnector interface { | ||||
| 	LoginURL(callbackURL, state string) (string, error) | ||||
| 	HandleCallback(r *http.Request) (identity storage.Identity, state string, err error) | ||||
| 	HandleCallback(r *http.Request) (identity Identity, state string, err error) | ||||
| } | ||||
|  | ||||
| // GroupsConnector is an optional interface for connectors which can map a user to groups. | ||||
| type GroupsConnector interface { | ||||
| 	Groups(identity storage.Identity) ([]string, error) | ||||
| 	Groups(identity Identity) ([]string, error) | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,6 @@ import ( | ||||
| 	"golang.org/x/oauth2/github" | ||||
|  | ||||
| 	"github.com/coreos/poke/connector" | ||||
| 	"github.com/coreos/poke/storage" | ||||
| ) | ||||
|  | ||||
| const baseURL = "https://api.github.com" | ||||
| @@ -85,7 +84,7 @@ func (e *oauth2Error) Error() string { | ||||
| 	return e.error + ": " + e.errorDescription | ||||
| } | ||||
|  | ||||
| func (c *githubConnector) HandleCallback(r *http.Request) (identity storage.Identity, state string, err error) { | ||||
| func (c *githubConnector) HandleCallback(r *http.Request) (identity connector.Identity, state string, err error) { | ||||
| 	q := r.URL.Query() | ||||
| 	if errType := q.Get("error"); errType != "" { | ||||
| 		return identity, "", &oauth2Error{errType, q.Get("error_description")} | ||||
| @@ -128,7 +127,7 @@ func (c *githubConnector) HandleCallback(r *http.Request) (identity storage.Iden | ||||
| 	if username == "" { | ||||
| 		username = user.Login | ||||
| 	} | ||||
| 	identity = storage.Identity{ | ||||
| 	identity = connector.Identity{ | ||||
| 		UserID:        strconv.Itoa(user.ID), | ||||
| 		Username:      username, | ||||
| 		Email:         user.Email, | ||||
| @@ -138,7 +137,7 @@ func (c *githubConnector) HandleCallback(r *http.Request) (identity storage.Iden | ||||
| 	return identity, q.Get("state"), nil | ||||
| } | ||||
|  | ||||
| func (c *githubConnector) Groups(identity storage.Identity) ([]string, error) { | ||||
| func (c *githubConnector) Groups(identity connector.Identity) ([]string, error) { | ||||
| 	var data connectorData | ||||
| 	if err := json.Unmarshal(identity.ConnectorData, &data); err != nil { | ||||
| 		return nil, fmt.Errorf("decode connector data: %v", err) | ||||
|   | ||||
| @@ -8,7 +8,6 @@ import ( | ||||
| 	"gopkg.in/ldap.v2" | ||||
|  | ||||
| 	"github.com/coreos/poke/connector" | ||||
| 	"github.com/coreos/poke/storage" | ||||
| ) | ||||
|  | ||||
| // Config holds the configuration parameters for the LDAP connector. | ||||
| @@ -32,6 +31,8 @@ type ldapConnector struct { | ||||
| 	Config | ||||
| } | ||||
|  | ||||
| var _ connector.PasswordConnector = (*ldapConnector)(nil) | ||||
|  | ||||
| func (c *ldapConnector) do(f func(c *ldap.Conn) error) error { | ||||
| 	// TODO(ericchiang): Connection pooling. | ||||
| 	conn, err := ldap.Dial("tcp", c.Host) | ||||
| @@ -43,15 +44,16 @@ func (c *ldapConnector) do(f func(c *ldap.Conn) error) error { | ||||
| 	return f(conn) | ||||
| } | ||||
|  | ||||
| func (c *ldapConnector) Login(username, password string) (storage.Identity, error) { | ||||
| func (c *ldapConnector) Login(username, password string) (connector.Identity, bool, error) { | ||||
| 	err := c.do(func(conn *ldap.Conn) error { | ||||
| 		return conn.Bind(fmt.Sprintf("uid=%s,%s", username, c.BindDN), password) | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return storage.Identity{}, err | ||||
| 		// TODO(ericchiang): Determine when the user has entered invalid credentials. | ||||
| 		return connector.Identity{}, false, err | ||||
| 	} | ||||
|  | ||||
| 	return storage.Identity{Username: username}, nil | ||||
| 	return connector.Identity{Username: username}, true, nil | ||||
| } | ||||
|  | ||||
| func (c *ldapConnector) Close() error { | ||||
|   | ||||
| @@ -2,12 +2,13 @@ | ||||
| package mock | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
|  | ||||
| 	"github.com/coreos/poke/connector" | ||||
| 	"github.com/coreos/poke/storage" | ||||
| ) | ||||
|  | ||||
| // New returns a mock connector which requires no user interaction. It always returns | ||||
| @@ -16,6 +17,11 @@ func New() connector.Connector { | ||||
| 	return mockConnector{} | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	_ connector.CallbackConnector = mockConnector{} | ||||
| 	_ connector.GroupsConnector   = mockConnector{} | ||||
| ) | ||||
|  | ||||
| type mockConnector struct{} | ||||
|  | ||||
| func (m mockConnector) Close() error { return nil } | ||||
| @@ -31,16 +37,22 @@ func (m mockConnector) LoginURL(callbackURL, state string) (string, error) { | ||||
| 	return u.String(), nil | ||||
| } | ||||
|  | ||||
| func (m mockConnector) HandleCallback(r *http.Request) (storage.Identity, string, error) { | ||||
| 	return storage.Identity{ | ||||
| var connectorData = []byte("foobar") | ||||
|  | ||||
| func (m mockConnector) HandleCallback(r *http.Request) (connector.Identity, string, error) { | ||||
| 	return connector.Identity{ | ||||
| 		UserID:        "0-385-28089-0", | ||||
| 		Username:      "Kilgore Trout", | ||||
| 		Email:         "kilgore@kilgore.trout", | ||||
| 		EmailVerified: true, | ||||
| 		ConnectorData: connectorData, | ||||
| 	}, r.URL.Query().Get("state"), nil | ||||
| } | ||||
|  | ||||
| func (m mockConnector) Groups(identity storage.Identity) ([]string, error) { | ||||
| func (m mockConnector) Groups(identity connector.Identity) ([]string, error) { | ||||
| 	if !bytes.Equal(identity.ConnectorData, connectorData) { | ||||
| 		return nil, errors.New("connector data mismatch") | ||||
| 	} | ||||
| 	return []string{"authors"}, nil | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user