mirror of
https://github.com/netbirdio/dex.git
synced 2026-05-22 18:43:53 -07:00
initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
bin
|
||||
dist
|
||||
@@ -0,0 +1,55 @@
|
||||
PROJ="poke"
|
||||
ORG_PATH="github.com/coreos"
|
||||
REPO_PATH="$(ORG_PATH)/$(PROJ)"
|
||||
|
||||
export GOBIN=$(PWD)/bin
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
|
||||
GOOS=$(shell go env GOOS)
|
||||
GOARCH=$(shell go env GOARCH)
|
||||
|
||||
COMMIT=$(shell git rev-parse HEAD)
|
||||
|
||||
# check if the current commit has a matching tag
|
||||
TAG=$(shell git describe --exact-match --abbrev=0 --tags $(COMMIT) 2> /dev/null || true)
|
||||
|
||||
ifeq ($(TAG),)
|
||||
VERSION=$(TAG)
|
||||
else
|
||||
VERSION=$(COMMIT)
|
||||
endif
|
||||
|
||||
|
||||
build: bin/poke bin/pokectl
|
||||
|
||||
bin/poke: FORCE
|
||||
@go install $(REPO_PATH)/cmd/poke
|
||||
|
||||
bin/pokectl: FORCE
|
||||
@go install $(REPO_PATH)/cmd/pokectl
|
||||
|
||||
test:
|
||||
@go test -v $(shell go list ./... | grep -v '/vendor/')
|
||||
|
||||
testrace:
|
||||
@go test -v --race $(shell go list ./... | grep -v '/vendor/')
|
||||
|
||||
vet:
|
||||
@go vet $(shell go list ./... | grep -v '/vendor/')
|
||||
|
||||
fmt:
|
||||
@go fmt $(shell go list ./... | grep -v '/vendor/')
|
||||
|
||||
lint:
|
||||
@for package in $(shell go list ./... | grep -v '/vendor/'); do \
|
||||
golint $$package; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm bin/poke bin/pokectl
|
||||
|
||||
testall: testrace vet fmt lint
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: test testrace vet fmt lint testall
|
||||
@@ -0,0 +1,126 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/poke/connector"
|
||||
"github.com/coreos/poke/connector/github"
|
||||
"github.com/coreos/poke/connector/ldap"
|
||||
"github.com/coreos/poke/connector/mock"
|
||||
"github.com/coreos/poke/storage"
|
||||
"github.com/coreos/poke/storage/kubernetes"
|
||||
)
|
||||
|
||||
// Config is the config format for the main application.
|
||||
type Config struct {
|
||||
Issuer string `yaml:"issuer"`
|
||||
Storage Storage `yaml:"storage"`
|
||||
Connectors []Connector `yaml:"connectors"`
|
||||
Web Web `yaml:"web"`
|
||||
}
|
||||
|
||||
// Web is the config format for the HTTP server.
|
||||
type Web struct {
|
||||
HTTP string `yaml:"http"`
|
||||
HTTPS string `yaml:"https"`
|
||||
TLSCert string `yaml:"tlsCert"`
|
||||
TLSKey string `yaml:"tlsKey"`
|
||||
}
|
||||
|
||||
// Storage holds app's storage configuration.
|
||||
type Storage struct {
|
||||
Type string `yaml:"type"`
|
||||
Config StorageConfig `yaml:"config"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML allows Storage to unmarshal its config field dynamically
|
||||
// depending on the type of storage.
|
||||
func (s *Storage) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var storageMeta struct {
|
||||
Type string `yaml:"type"`
|
||||
}
|
||||
if err := unmarshal(&storageMeta); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Type = storageMeta.Type
|
||||
var c struct {
|
||||
Config StorageConfig `yaml:"config"`
|
||||
}
|
||||
switch storageMeta.Type {
|
||||
case "kubernetes":
|
||||
c.Config = &kubernetes.Config{}
|
||||
default:
|
||||
return fmt.Errorf("unknown storage type %q", storageMeta.Type)
|
||||
}
|
||||
if err := unmarshal(c); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Config = c.Config
|
||||
return nil
|
||||
}
|
||||
|
||||
// StorageConfig is a configuration that can create a storage.
|
||||
type StorageConfig interface {
|
||||
Open() (storage.Storage, error)
|
||||
}
|
||||
|
||||
// Connector is a magical type that can unmarshal YAML dynamically. The
|
||||
// Type field determines the connector type, which is then customized for Config.
|
||||
type Connector struct {
|
||||
Type string `yaml:"type"`
|
||||
Name string `yaml:"name"`
|
||||
ID string `yaml:"id"`
|
||||
|
||||
Config ConnectorConfig `yaml:"config"`
|
||||
}
|
||||
|
||||
// ConnectorConfig is a configuration that can open a connector.
|
||||
type ConnectorConfig interface {
|
||||
Open() (connector.Connector, error)
|
||||
}
|
||||
|
||||
// UnmarshalYAML allows Connector to unmarshal its config field dynamically
|
||||
// depending on the type of connector.
|
||||
func (c *Connector) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var connectorMetadata struct {
|
||||
Type string `yaml:"type"`
|
||||
Name string `yaml:"name"`
|
||||
ID string `yaml:"id"`
|
||||
}
|
||||
if err := unmarshal(&connectorMetadata); err != nil {
|
||||
return err
|
||||
}
|
||||
c.Type = connectorMetadata.Type
|
||||
c.Name = connectorMetadata.Name
|
||||
c.ID = connectorMetadata.ID
|
||||
|
||||
switch c.Type {
|
||||
case "mock":
|
||||
var config struct {
|
||||
Config mock.Config `yaml:"config"`
|
||||
}
|
||||
if err := unmarshal(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
c.Config = &config.Config
|
||||
case "ldap":
|
||||
var config struct {
|
||||
Config ldap.Config `yaml:"config"`
|
||||
}
|
||||
if err := unmarshal(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
c.Config = &config.Config
|
||||
case "github":
|
||||
var config struct {
|
||||
Config github.Config `yaml:"config"`
|
||||
}
|
||||
if err := unmarshal(&config); err != nil {
|
||||
return err
|
||||
}
|
||||
c.Config = &config.Config
|
||||
default:
|
||||
return fmt.Errorf("unknown connector type %q", c.Type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package main
|
||||
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func commandRoot() *cobra.Command {
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "poke",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmd.Help()
|
||||
os.Exit(2)
|
||||
},
|
||||
}
|
||||
rootCmd.AddCommand(commandServe())
|
||||
rootCmd.AddCommand(commandVersion())
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := commandRoot().Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/coreos/poke/server"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func commandServe() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "serve [ config file ]",
|
||||
Short: "Connect to the storage and begin serving requests.",
|
||||
Long: ``,
|
||||
Example: "poke serve c.yaml",
|
||||
RunE: serve,
|
||||
}
|
||||
}
|
||||
|
||||
func serve(cmd *cobra.Command, args []string) error {
|
||||
switch len(args) {
|
||||
default:
|
||||
return errors.New("surplus arguments")
|
||||
case 0:
|
||||
// TODO(ericchiang): Consider having a default config file location.
|
||||
return errors.New("no config file specified")
|
||||
case 1:
|
||||
}
|
||||
|
||||
configFile := args[0]
|
||||
configData, err := ioutil.ReadFile(configFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read config file %s: %v", configFile, err)
|
||||
}
|
||||
|
||||
var c Config
|
||||
if err := yaml.Unmarshal(configData, &c); err != nil {
|
||||
return fmt.Errorf("parse config file %s: %v", configFile, err)
|
||||
}
|
||||
|
||||
// Fast checks. Perform these first for a more responsive CLI.
|
||||
checks := []struct {
|
||||
bad bool
|
||||
errMsg string
|
||||
}{
|
||||
{c.Issuer == "", "no issuer specified in config file"},
|
||||
{len(c.Connectors) == 0, "no connectors supplied in config file"},
|
||||
{c.Storage.Config == nil, "no storage suppied in config file"},
|
||||
{c.Web.HTTP == "" && c.Web.HTTPS == "", "must supply a HTTP/HTTPS address to listen on"},
|
||||
{c.Web.HTTPS != "" && c.Web.TLSCert == "", "no cert specified for HTTPS"},
|
||||
{c.Web.HTTPS != "" && c.Web.TLSKey == "", "no private key specified for HTTPS"},
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
if check.bad {
|
||||
return errors.New(check.errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
connectors := make([]server.Connector, len(c.Connectors))
|
||||
for i, conn := range c.Connectors {
|
||||
if conn.Config == nil {
|
||||
return fmt.Errorf("no config field for connector %q", conn.ID)
|
||||
}
|
||||
c, err := conn.Config.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("open %s: %v", conn.ID, err)
|
||||
}
|
||||
connectors[i] = server.Connector{
|
||||
ID: conn.ID,
|
||||
DisplayName: conn.Name,
|
||||
Connector: c,
|
||||
}
|
||||
}
|
||||
|
||||
s, err := c.Storage.Config.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing storage: %v", err)
|
||||
}
|
||||
|
||||
serverConfig := server.Config{
|
||||
Issuer: c.Issuer,
|
||||
Connectors: connectors,
|
||||
Storage: s,
|
||||
}
|
||||
|
||||
serv, err := server.New(serverConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing server: %v", err)
|
||||
}
|
||||
errc := make(chan error, 2)
|
||||
if c.Web.HTTP != "" {
|
||||
go func() {
|
||||
log.Printf("listening on %s", c.Web.HTTP)
|
||||
errc <- http.ListenAndServe(c.Web.HTTP, serv)
|
||||
}()
|
||||
}
|
||||
if c.Web.HTTPS != "" {
|
||||
go func() {
|
||||
log.Printf("listening on %s", c.Web.HTTPS)
|
||||
errc <- http.ListenAndServeTLS(c.Web.HTTPS, c.Web.TLSCert, c.Web.TLSKey, serv)
|
||||
}()
|
||||
}
|
||||
return <-errc
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/coreos/poke/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func commandVersion() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf(`v%s %s %s %s
|
||||
`, version.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "pokectl",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Package connector defines interfaces for federated identity strategies.
|
||||
package connector
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/coreos/poke/storage"
|
||||
)
|
||||
|
||||
// Connector is a mechanism for federating login to a remote identity service.
|
||||
//
|
||||
// Implementations are expected to implement either the PasswordConnector or
|
||||
// CallbackConnector interface.
|
||||
type Connector interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
// PasswordConnector is an optional interface for password based connectors.
|
||||
type PasswordConnector interface {
|
||||
Login(username, password string) (identity storage.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)
|
||||
}
|
||||
|
||||
// GroupsConnector is an optional interface for connectors which can map a user to groups.
|
||||
type GroupsConnector interface {
|
||||
Groups(identity storage.Identity) ([]string, error)
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
// Package github provides authentication strategies using GitHub.
|
||||
package github
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/github"
|
||||
|
||||
"github.com/coreos/poke/connector"
|
||||
"github.com/coreos/poke/storage"
|
||||
)
|
||||
|
||||
const baseURL = "https://api.github.com"
|
||||
|
||||
// Config holds configuration options for github logins.
|
||||
type Config struct {
|
||||
ClientID string `yaml:"clientID"`
|
||||
ClientSecret string `yaml:"clientSecret"`
|
||||
RedirectURI string `yaml:"redirectURI"`
|
||||
Org string `yaml:"org"`
|
||||
}
|
||||
|
||||
// Open returns a strategy for logging in through GitHub.
|
||||
func (c *Config) Open() (connector.Connector, error) {
|
||||
return &githubConnector{
|
||||
redirectURI: c.RedirectURI,
|
||||
org: c.Org,
|
||||
oauth2Config: &oauth2.Config{
|
||||
ClientID: os.ExpandEnv(c.ClientID),
|
||||
ClientSecret: os.ExpandEnv(c.ClientSecret),
|
||||
Endpoint: github.Endpoint,
|
||||
Scopes: []string{
|
||||
"user:email", // View user's email
|
||||
"read:org", // View user's org teams.
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type connectorData struct {
|
||||
// GitHub's OAuth2 tokens never expire. We don't need a refresh token.
|
||||
AccessToken string `json:"accessToken"`
|
||||
}
|
||||
|
||||
var (
|
||||
_ connector.CallbackConnector = (*githubConnector)(nil)
|
||||
_ connector.GroupsConnector = (*githubConnector)(nil)
|
||||
)
|
||||
|
||||
type githubConnector struct {
|
||||
redirectURI string
|
||||
org string
|
||||
oauth2Config *oauth2.Config
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (c *githubConnector) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *githubConnector) LoginURL(callbackURL, state string) (string, error) {
|
||||
if c.redirectURI != callbackURL {
|
||||
return "", fmt.Errorf("expected callback URL did not match the URL in the config")
|
||||
}
|
||||
return c.oauth2Config.AuthCodeURL(state), nil
|
||||
}
|
||||
|
||||
type oauth2Error struct {
|
||||
error string
|
||||
errorDescription string
|
||||
}
|
||||
|
||||
func (e *oauth2Error) Error() string {
|
||||
if e.errorDescription == "" {
|
||||
return e.error
|
||||
}
|
||||
return e.error + ": " + e.errorDescription
|
||||
}
|
||||
|
||||
func (c *githubConnector) HandleCallback(r *http.Request) (identity storage.Identity, state string, err error) {
|
||||
q := r.URL.Query()
|
||||
if errType := q.Get("error"); errType != "" {
|
||||
return identity, "", &oauth2Error{errType, q.Get("error_description")}
|
||||
}
|
||||
token, err := c.oauth2Config.Exchange(c.ctx, q.Get("code"))
|
||||
if err != nil {
|
||||
return identity, "", fmt.Errorf("github: failed to get token: %v", err)
|
||||
}
|
||||
|
||||
resp, err := c.oauth2Config.Client(c.ctx, token).Get(baseURL + "/user")
|
||||
if err != nil {
|
||||
return identity, "", fmt.Errorf("github: get URL %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return identity, "", fmt.Errorf("github: read body: %v", err)
|
||||
}
|
||||
return identity, "", fmt.Errorf("%s: %s", resp.Status, body)
|
||||
}
|
||||
var user struct {
|
||||
Name string `json:"name"`
|
||||
Login string `json:"login"`
|
||||
ID int `json:"id"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&user); err != nil {
|
||||
return identity, "", fmt.Errorf("failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
data := connectorData{AccessToken: token.AccessToken}
|
||||
connData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return identity, "", fmt.Errorf("marshal connector data: %v", err)
|
||||
}
|
||||
|
||||
username := user.Name
|
||||
if username == "" {
|
||||
username = user.Login
|
||||
}
|
||||
identity = storage.Identity{
|
||||
UserID: strconv.Itoa(user.ID),
|
||||
Username: username,
|
||||
Email: user.Email,
|
||||
EmailVerified: true,
|
||||
ConnectorData: connData,
|
||||
}
|
||||
return identity, q.Get("state"), nil
|
||||
}
|
||||
|
||||
func (c *githubConnector) Groups(identity storage.Identity) ([]string, error) {
|
||||
var data connectorData
|
||||
if err := json.Unmarshal(identity.ConnectorData, &data); err != nil {
|
||||
return nil, fmt.Errorf("decode connector data: %v", err)
|
||||
}
|
||||
token := &oauth2.Token{AccessToken: data.AccessToken}
|
||||
resp, err := c.oauth2Config.Client(c.ctx, token).Get(baseURL + "/user/teams")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("github: get teams: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("github: read body: %v", err)
|
||||
}
|
||||
return nil, fmt.Errorf("%s: %s", resp.Status, body)
|
||||
}
|
||||
|
||||
// https://developer.github.com/v3/orgs/teams/#response-12
|
||||
var teams []struct {
|
||||
Name string `json:"name"`
|
||||
Org struct {
|
||||
Login string `json:"login"`
|
||||
} `json:"organization"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&teams); err != nil {
|
||||
return nil, fmt.Errorf("github: unmarshal groups: %v", err)
|
||||
}
|
||||
groups := []string{}
|
||||
for _, team := range teams {
|
||||
if team.Org.Login == c.org {
|
||||
groups = append(groups, team.Name)
|
||||
}
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Package ldap implements strategies for authenticating using the LDAP protocol.
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/ldap.v2"
|
||||
|
||||
"github.com/coreos/poke/connector"
|
||||
"github.com/coreos/poke/storage"
|
||||
)
|
||||
|
||||
// Config holds the configuration parameters for the LDAP connector.
|
||||
type Config struct {
|
||||
Host string `yaml:"host"`
|
||||
BindDN string `yaml:"bindDN"`
|
||||
}
|
||||
|
||||
// Open returns an authentication strategy using LDAP.
|
||||
func (c *Config) Open() (connector.Connector, error) {
|
||||
if c.Host == "" {
|
||||
return nil, errors.New("missing host parameter")
|
||||
}
|
||||
if c.BindDN == "" {
|
||||
return nil, errors.New("missing bindDN paramater")
|
||||
}
|
||||
return &ldapConnector{*c}, nil
|
||||
}
|
||||
|
||||
type ldapConnector struct {
|
||||
Config
|
||||
}
|
||||
|
||||
func (c *ldapConnector) do(f func(c *ldap.Conn) error) error {
|
||||
// TODO(ericchiang): Connection pooling.
|
||||
conn, err := ldap.Dial("tcp", c.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
return f(conn)
|
||||
}
|
||||
|
||||
func (c *ldapConnector) Login(username, password string) (storage.Identity, 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
|
||||
}
|
||||
|
||||
return storage.Identity{Username: username}, nil
|
||||
}
|
||||
|
||||
func (c *ldapConnector) Close() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Package mock implements a mock connector which requires no user interaction.
|
||||
package mock
|
||||
|
||||
import (
|
||||
"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
|
||||
// the same (fake) identity.
|
||||
func New() connector.Connector {
|
||||
return mockConnector{}
|
||||
}
|
||||
|
||||
type mockConnector struct{}
|
||||
|
||||
func (m mockConnector) Close() error { return nil }
|
||||
|
||||
func (m mockConnector) LoginURL(callbackURL, state string) (string, error) {
|
||||
u, err := url.Parse(callbackURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse callbackURL %q: %v", callbackURL, err)
|
||||
}
|
||||
v := u.Query()
|
||||
v.Set("state", state)
|
||||
u.RawQuery = v.Encode()
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (m mockConnector) HandleCallback(r *http.Request) (storage.Identity, string, error) {
|
||||
return storage.Identity{
|
||||
UserID: "0-385-28089-0",
|
||||
Username: "Kilgore Trout",
|
||||
Email: "kilgore@kilgore.trout",
|
||||
EmailVerified: true,
|
||||
}, r.URL.Query().Get("state"), nil
|
||||
}
|
||||
|
||||
func (m mockConnector) Groups(identity storage.Identity) ([]string, error) {
|
||||
return []string{"authors"}, nil
|
||||
}
|
||||
|
||||
// Config holds the configuration parameters for the mock connector.
|
||||
type Config struct{}
|
||||
|
||||
// Open returns an authentication strategy which requires no user interaction.
|
||||
func (c *Config) Open() (connector.Connector, error) {
|
||||
return New(), nil
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Package oidc implements logging in through OpenID Connect providers.
|
||||
package oidc
|
||||
@@ -0,0 +1,9 @@
|
||||
kind: OAuth2Client
|
||||
apiVersion: oauth2clients.oidc.coreos.com/v1
|
||||
metadata:
|
||||
name: example-app
|
||||
namespace: default
|
||||
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
|
||||
redirectURIs:
|
||||
- http://127.0.0.1:5555/callback
|
||||
name: Example App
|
||||
@@ -0,0 +1,20 @@
|
||||
issuer: http://127.0.0.1:5556
|
||||
storage:
|
||||
type: kubernetes
|
||||
|
||||
web:
|
||||
http: 127.0.0.1:5556
|
||||
|
||||
connectors:
|
||||
- type: mock
|
||||
id: mock
|
||||
name: Mock
|
||||
|
||||
- type: github
|
||||
id: github
|
||||
name: GitHub
|
||||
config:
|
||||
clientID: "$GITHUB_CLIENT_ID"
|
||||
clientSecret: "$GITHUB_CLIENT_SECRET"
|
||||
redirectURI: http://127.0.0.1:5556/callback/github
|
||||
org: kubernetes
|
||||
@@ -0,0 +1,48 @@
|
||||
# NOTE: Because of a bug in third party resources, each resource must be in it's
|
||||
# own API Group.
|
||||
#
|
||||
# See fix at https://github.com/kubernetes/kubernetes/pull/28414
|
||||
|
||||
metadata:
|
||||
name: auth-code.authcodes.oidc.coreos.com
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ThirdPartyResource
|
||||
description: "A code which can be claimed for an access token."
|
||||
versions:
|
||||
- name: v1
|
||||
---
|
||||
|
||||
metadata:
|
||||
name: auth-request.authrequests.oidc.coreos.com
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ThirdPartyResource
|
||||
description: "A request for an end user to authorize a client."
|
||||
versions:
|
||||
- name: v1
|
||||
---
|
||||
|
||||
metadata:
|
||||
name: o-auth2-client.oauth2clients.oidc.coreos.com
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ThirdPartyResource
|
||||
description: "An OpenID Connect client."
|
||||
versions:
|
||||
- name: v1
|
||||
---
|
||||
|
||||
metadata:
|
||||
name: signing-key.signingkeies.oidc.coreos.com
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ThirdPartyResource
|
||||
description: "Keys used to sign and verify OpenID Connect tokens."
|
||||
versions:
|
||||
- name: v1
|
||||
---
|
||||
|
||||
metadata:
|
||||
name: refresh-token.refreshtokens.oidc.coreos.com
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ThirdPartyResource
|
||||
description: "Refresh tokens for clients to continuously act on behalf of an end user."
|
||||
versions:
|
||||
- name: v1
|
||||
Generated
+67
@@ -0,0 +1,67 @@
|
||||
hash: 8b33b8abf5bca183ffa7108b03f4001f4d23f7f2e26f773f5bcb4bfefa51a26f
|
||||
updated: 2016-07-22T23:25:33.173188655-07:00
|
||||
imports:
|
||||
- name: github.com/ericchiang/oidc
|
||||
version: 69fec81d167d815f4f455c741b2a94ffaf547ed2
|
||||
- name: github.com/golang/protobuf
|
||||
version: 874264fbbb43f4d91e999fecb4b40143ed611400
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/gorilla/context
|
||||
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
|
||||
- name: github.com/gorilla/mux
|
||||
version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
|
||||
- name: github.com/gtank/cryptopasta
|
||||
version: e7e23673cac3f529f49e22f94e4af6d12bb49dba
|
||||
- name: github.com/inconshreveable/mousetrap
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
- name: github.com/mitchellh/go-homedir
|
||||
version: 756f7b183b7ab78acdbbee5c7f392838ed459dda
|
||||
- name: github.com/pquerna/cachecontrol
|
||||
version: c97913dcbd76de40b051a9b4cd827f7eaeb7a868
|
||||
subpackages:
|
||||
- cacheobject
|
||||
- name: github.com/spf13/cobra
|
||||
version: bc81c21bd0d8be5ba2d6630a505d79d4467566e7
|
||||
- name: github.com/spf13/pflag
|
||||
version: 367864438f1b1a3c7db4da06a2f55b144e6784e0
|
||||
- name: golang.org/x/crypto
|
||||
version: 2c99acdd1e9b90d779ca23f632aad86af9909c62
|
||||
subpackages:
|
||||
- bcrypt
|
||||
- blowfish
|
||||
- name: golang.org/x/net
|
||||
version: d7bf3545bb0dacf009c535b3d3fbf53ac0a339ab
|
||||
subpackages:
|
||||
- context
|
||||
- name: golang.org/x/oauth2
|
||||
version: 08c8d727d2392d18286f9f88ad775ad98f09ab33
|
||||
subpackages:
|
||||
- internal
|
||||
- name: google.golang.org/appengine
|
||||
version: 267c27e7492265b84fc6719503b14a1e17975d79
|
||||
subpackages:
|
||||
- urlfetch
|
||||
- internal
|
||||
- internal/urlfetch
|
||||
- internal/base
|
||||
- internal/datastore
|
||||
- internal/log
|
||||
- internal/remote_api
|
||||
- name: gopkg.in/asn1-ber.v1
|
||||
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
|
||||
- name: gopkg.in/ldap.v2
|
||||
version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73
|
||||
- name: gopkg.in/square/go-jose.v1
|
||||
version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc
|
||||
subpackages:
|
||||
- cipher
|
||||
- json
|
||||
- name: gopkg.in/square/go-jose.v2
|
||||
version: f209f41628247c56938cb20ef51d589ddad6c30b
|
||||
subpackages:
|
||||
- cipher
|
||||
- json
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: a83829b6f1293c91addabc89d0571c246397bbf4
|
||||
testImports: []
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package: github.com/coreos/poke
|
||||
import:
|
||||
- package: github.com/spf13/cobra
|
||||
version: bc81c21bd0d8be5ba2d6630a505d79d4467566e7
|
||||
- package: github.com/spf13/pflag
|
||||
version: 367864438f1b1a3c7db4da06a2f55b144e6784e0
|
||||
- package: github.com/inconshreveable/mousetrap
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
|
||||
- package: gopkg.in/ldap.v2
|
||||
version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73
|
||||
- package: gopkg.in/asn1-ber.v1
|
||||
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
|
||||
|
||||
- package: gopkg.in/square/go-jose.v2
|
||||
version: f209f41628247c56938cb20ef51d589ddad6c30b
|
||||
subpackages:
|
||||
- cipher
|
||||
- json
|
||||
|
||||
- package: gopkg.in/yaml.v2
|
||||
version: a83829b6f1293c91addabc89d0571c246397bbf4
|
||||
|
||||
- package: golang.org/x/net/context
|
||||
version: d7bf3545bb0dacf009c535b3d3fbf53ac0a339ab
|
||||
|
||||
- package: github.com/gorilla/mux
|
||||
version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
|
||||
- package: github.com/gorilla/context
|
||||
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
|
||||
|
||||
- package: github.com/gtank/cryptopasta
|
||||
version: e7e23673cac3f529f49e22f94e4af6d12bb49dba
|
||||
- package: golang.org/x/crypto
|
||||
version: 2c99acdd1e9b90d779ca23f632aad86af9909c62
|
||||
subpackages:
|
||||
- bcrypt
|
||||
|
||||
- package: github.com/ericchiang/oidc
|
||||
version: 69fec81d167d815f4f455c741b2a94ffaf547ed2
|
||||
- package: github.com/pquerna/cachecontrol
|
||||
version: c97913dcbd76de40b051a9b4cd827f7eaeb7a868
|
||||
- package: gopkg.in/square/go-jose.v1
|
||||
version: v1.0.2
|
||||
- package: golang.org/x/oauth2
|
||||
version: 08c8d727d2392d18286f9f88ad775ad98f09ab33
|
||||
- package: google.golang.org/appengine
|
||||
version: 267c27e7492265b84fc6719503b14a1e17975d79
|
||||
subpackages:
|
||||
- urlfetch
|
||||
- internal
|
||||
- internal/urlfetch
|
||||
- internal/base
|
||||
- internal/datastore
|
||||
- internal/log
|
||||
- internal/remote_api
|
||||
- package: github.com/golang/protobuf
|
||||
version: 874264fbbb43f4d91e999fecb4b40143ed611400
|
||||
subpackages:
|
||||
- proto
|
||||
|
||||
- package: github.com/mitchellh/go-homedir
|
||||
verison: 756f7b183b7ab78acdbbee5c7f392838ed459dda
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type glideLock struct {
|
||||
Imports []struct {
|
||||
Name string `yaml:"name"`
|
||||
Subpackages []string `yaml:"subpackages,omitempty"`
|
||||
} `yaml:"imports"`
|
||||
TestImports []struct {
|
||||
Name string `yaml:"name"`
|
||||
Subpackages []string `yaml:"subpackages,omitempty"`
|
||||
} `yaml:"testImports"`
|
||||
}
|
||||
|
||||
type glideYAML struct {
|
||||
Imports []struct {
|
||||
Name string `yaml:"package"`
|
||||
} `yaml:"import"`
|
||||
}
|
||||
|
||||
func loadYAML(t *testing.T, file string, v interface{}) {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
t.Fatalf("read file %s: %v", file, err)
|
||||
}
|
||||
if err := yaml.Unmarshal(data, v); err != nil {
|
||||
t.Fatalf("unmarshal file %s: %v", file, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TestGlideYAMLPinsAllDependencies ensures that all packages listed in glide.lock also
|
||||
// appear in glide.yaml which can get out of sync if glide.yaml fails to list transitive
|
||||
// dependencies.
|
||||
//
|
||||
// Testing this ensures developers can update individual packages without grabbing the HEAD
|
||||
// of an unspecified dependency.
|
||||
func TestGlideYAMLPinsAllDependencies(t *testing.T) {
|
||||
var (
|
||||
lockPackages glideLock
|
||||
yamlPackages glideYAML
|
||||
)
|
||||
loadYAML(t, "glide.lock", &lockPackages)
|
||||
loadYAML(t, "glide.yaml", &yamlPackages)
|
||||
|
||||
if len(yamlPackages.Imports) == 0 {
|
||||
t.Fatalf("no packages found in glide.yaml")
|
||||
}
|
||||
|
||||
pkgs := make(map[string]bool)
|
||||
for _, pkg := range yamlPackages.Imports {
|
||||
pkgs[pkg.Name] = true
|
||||
}
|
||||
|
||||
for _, pkg := range lockPackages.Imports {
|
||||
if pkgs[pkg.Name] {
|
||||
continue
|
||||
}
|
||||
if len(pkg.Subpackages) == 0 {
|
||||
t.Errorf("package in glide lock but not pinned in glide yaml: %s", pkg.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, subpkg := range pkg.Subpackages {
|
||||
pkgName := path.Join(pkg.Name, subpkg)
|
||||
if !pkgs[pkgName] {
|
||||
t.Errorf("package in glide lock but not pinned in glide yaml: %s", pkgName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range lockPackages.TestImports {
|
||||
if pkgs[pkg.Name] {
|
||||
continue
|
||||
}
|
||||
if len(pkg.Subpackages) == 0 {
|
||||
t.Errorf("package in glide lock but not pinned in glide yaml: %s", pkg.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, subpkg := range pkg.Subpackages {
|
||||
pkgName := path.Join(pkg.Name, subpkg)
|
||||
if !pkgs[pkgName] {
|
||||
t.Errorf("package in glide lock but not pinned in glide yaml: %s", pkgName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveVersionControl(t *testing.T) {
|
||||
err := filepath.Walk("vendor", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
t.Fatalf("walk: stat path %s failed: %v", path, err)
|
||||
}
|
||||
if info.IsDir() && filepath.Base(path) == ".git" {
|
||||
t.Fatalf(".git directory detected in vendor: %s. Revendor packages and remove version control data with 'glide update -s -v -u'", path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("walk: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Package server implements an OpenID Connect server with federated logins.
|
||||
package server
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user