*: 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