mirror of https://github.com/dexidp/dex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
709 lines
16 KiB
709 lines
16 KiB
package integration |
|
|
|
import ( |
|
"encoding/base64" |
|
"fmt" |
|
"net/http" |
|
"net/http/httptest" |
|
"net/url" |
|
"testing" |
|
|
|
"github.com/coreos/go-oidc/oidc" |
|
"github.com/kylelemons/godebug/pretty" |
|
"google.golang.org/api/googleapi" |
|
|
|
"github.com/coreos/dex/admin" |
|
"github.com/coreos/dex/client" |
|
"github.com/coreos/dex/client/manager" |
|
"github.com/coreos/dex/db" |
|
"github.com/coreos/dex/schema/adminschema" |
|
"github.com/coreos/dex/server" |
|
"github.com/coreos/dex/user" |
|
) |
|
|
|
const ( |
|
adminAPITestSecret = "admin_secret" |
|
) |
|
|
|
type adminAPITestFixtures struct { |
|
ur user.UserRepo |
|
pwr user.PasswordInfoRepo |
|
cr client.ClientRepo |
|
adAPI *admin.AdminAPI |
|
adSrv *server.AdminServer |
|
hSrv *httptest.Server |
|
hc *http.Client |
|
adClient *adminschema.Service |
|
} |
|
|
|
func (t *adminAPITestFixtures) close() { |
|
t.hSrv.Close() |
|
} |
|
|
|
var ( |
|
adminUsers = []user.UserWithRemoteIdentities{ |
|
{ |
|
User: user.User{ |
|
ID: "ID-1", |
|
Email: "Email-1@example.com", |
|
}, |
|
}, |
|
{ |
|
User: user.User{ |
|
ID: "ID-2", |
|
Email: "Email-2@example.com", |
|
}, |
|
}, |
|
{ |
|
User: user.User{ |
|
ID: "ID-3", |
|
Email: "Email-3@example.com", |
|
}, |
|
}, |
|
} |
|
|
|
adminPasswords = []user.PasswordInfo{ |
|
{ |
|
UserID: "ID-1", |
|
Password: []byte("hi."), |
|
}, |
|
} |
|
|
|
clients = []client.Client{ |
|
{ |
|
Credentials: oidc.ClientCredentials{ |
|
ID: "client-1", |
|
Secret: "Zm9vYmFy", // "foobar" |
|
}, |
|
Metadata: oidc.ClientMetadata{ |
|
RedirectURIs: []url.URL{ |
|
url.URL{Scheme: "http", Host: "127.0.0.1:5556", Path: "/cb"}, |
|
url.URL{Scheme: "https", Host: "example.com", Path: "/callback"}, |
|
}, |
|
}, |
|
Admin: true, |
|
}, |
|
} |
|
) |
|
|
|
type adminAPITransport struct { |
|
secret string |
|
} |
|
|
|
func (a *adminAPITransport) RoundTrip(r *http.Request) (*http.Response, error) { |
|
r.Header.Set("Authorization", a.secret) |
|
return http.DefaultTransport.RoundTrip(r) |
|
} |
|
|
|
func makeAdminAPITestFixtures() *adminAPITestFixtures { |
|
f := &adminAPITestFixtures{} |
|
|
|
dbMap, ur, pwr, um := makeUserObjects(adminUsers, adminPasswords) |
|
|
|
var cliCount int |
|
secGen := func() ([]byte, error) { |
|
id := []byte(fmt.Sprintf("client_%v", cliCount)) |
|
cliCount++ |
|
return id, nil |
|
} |
|
cr := db.NewClientRepo(dbMap) |
|
clientIDGenerator := func(hostport string) (string, error) { |
|
return fmt.Sprintf("client_%v", hostport), nil |
|
} |
|
for _, client := range clients { |
|
_, err := cr.New(nil, client) |
|
if err != nil { |
|
panic(err) |
|
} |
|
} |
|
cm := manager.NewClientManager(cr, db.TransactionFactory(dbMap), manager.ManagerOptions{SecretGenerator: secGen, ClientIDGenerator: clientIDGenerator}) |
|
ccr := db.NewConnectorConfigRepo(dbMap) |
|
|
|
f.cr = cr |
|
f.ur = ur |
|
f.pwr = pwr |
|
f.adAPI = admin.NewAdminAPI(ur, pwr, cr, ccr, um, cm, "local") |
|
f.adSrv = server.NewAdminServer(f.adAPI, nil, adminAPITestSecret) |
|
f.hSrv = httptest.NewServer(f.adSrv.HTTPHandler()) |
|
f.hc = &http.Client{ |
|
Transport: &adminAPITransport{ |
|
secret: adminAPITestSecret, |
|
}, |
|
} |
|
f.adClient, _ = adminschema.NewWithBasePath(f.hc, f.hSrv.URL) |
|
|
|
return f |
|
} |
|
|
|
func TestGetAdmin(t *testing.T) { |
|
|
|
tests := []struct { |
|
id string |
|
errCode int |
|
}{ |
|
{ |
|
id: "ID-1", |
|
errCode: -1, |
|
}, |
|
{ |
|
id: "ID-2", |
|
errCode: http.StatusNotFound, |
|
}, |
|
} |
|
|
|
for i, tt := range tests { |
|
func() { |
|
f := makeAdminAPITestFixtures() |
|
defer f.close() |
|
admn, err := f.adClient.Admin.Get(tt.id).Do() |
|
if tt.errCode != -1 { |
|
if err == nil { |
|
t.Errorf("case %d: err was nil", i) |
|
return |
|
} |
|
gErr, ok := err.(*googleapi.Error) |
|
if !ok { |
|
t.Errorf("case %d: not a googleapi Error: %q", i, err) |
|
return |
|
} |
|
|
|
if gErr.Code != tt.errCode { |
|
t.Errorf("case %d: want=%d, got=%d", i, tt.errCode, gErr.Code) |
|
return |
|
} |
|
} else { |
|
if err != nil { |
|
t.Errorf("case %d: err != nil: %q", i, err) |
|
} |
|
if admn == nil { |
|
t.Errorf("case %d: admn was nil", i) |
|
} |
|
|
|
if admn.Id != "ID-1" { |
|
t.Errorf("case %d: want=%q, got=%q", i, tt.id, admn.Id) |
|
} |
|
} |
|
}() |
|
} |
|
} |
|
|
|
func TestCreateAdmin(t *testing.T) { |
|
tests := []struct { |
|
admn *adminschema.Admin |
|
errCode int |
|
secret string |
|
noSecret bool |
|
}{ |
|
{ |
|
admn: &adminschema.Admin{ |
|
Email: "foo@example.com", |
|
Password: "foopass", |
|
}, |
|
errCode: -1, |
|
}, |
|
{ |
|
admn: &adminschema.Admin{ |
|
Email: "foo@example.com", |
|
Password: "foopass", |
|
}, |
|
errCode: http.StatusUnauthorized, |
|
secret: "bad_secret", |
|
}, |
|
{ |
|
admn: &adminschema.Admin{ |
|
Email: "foo@example.com", |
|
Password: "foopass", |
|
}, |
|
errCode: http.StatusUnauthorized, |
|
noSecret: true, |
|
}, |
|
{ |
|
// duplicate Email |
|
admn: &adminschema.Admin{ |
|
Email: "Email-1@example.com", |
|
Password: "foopass", |
|
}, |
|
errCode: http.StatusConflict, |
|
}, |
|
{ |
|
// missing Email |
|
admn: &adminschema.Admin{ |
|
Password: "foopass", |
|
}, |
|
errCode: http.StatusBadRequest, |
|
}, |
|
} |
|
for i, tt := range tests { |
|
func() { |
|
f := makeAdminAPITestFixtures() |
|
if tt.secret != "" { |
|
f.hc.Transport = &adminAPITransport{ |
|
secret: tt.secret, |
|
} |
|
} |
|
if tt.noSecret { |
|
f.hc.Transport = http.DefaultTransport |
|
} |
|
defer f.close() |
|
|
|
admn, err := f.adClient.Admin.Create(tt.admn).Do() |
|
if tt.errCode != -1 { |
|
if err == nil { |
|
t.Errorf("case %d: err was nil", i) |
|
return |
|
} |
|
gErr, ok := err.(*googleapi.Error) |
|
if !ok { |
|
t.Errorf("case %d: not a googleapi Error: %q", i, err) |
|
return |
|
} |
|
|
|
if gErr.Code != tt.errCode { |
|
t.Errorf("case %d: want=%d, got=%d", i, tt.errCode, gErr.Code) |
|
return |
|
} |
|
} else { |
|
if err != nil { |
|
t.Errorf("case %d: err != nil: %q", i, err) |
|
} |
|
|
|
tt.admn.Id = admn.Id |
|
if diff := pretty.Compare(tt.admn, admn); diff != "" { |
|
t.Errorf("case %d: Compare(want, got) = %v", i, diff) |
|
} |
|
|
|
gotAdmn, err := f.adClient.Admin.Get(admn.Id).Do() |
|
if err != nil { |
|
t.Errorf("case %d: err != nil: %q", i, err) |
|
} |
|
if diff := pretty.Compare(admn, gotAdmn); diff != "" { |
|
t.Errorf("case %d: Compare(want, got) = %v", i, diff) |
|
} |
|
|
|
usr, err := f.ur.GetByRemoteIdentity(nil, user.RemoteIdentity{ |
|
ConnectorID: "local", |
|
ID: tt.admn.Id, |
|
}) |
|
if err != nil { |
|
t.Errorf("case %d: err != nil: %q", i, err) |
|
} |
|
|
|
if usr.ID != tt.admn.Id { |
|
t.Errorf("case %d: want=%q, got=%q", i, tt.admn.Id, usr.ID) |
|
} |
|
|
|
} |
|
}() |
|
} |
|
} |
|
|
|
func TestConnectors(t *testing.T) { |
|
tests := []struct { |
|
req adminschema.ConnectorsSetRequest |
|
want adminschema.ConnectorsGetResponse |
|
wantErr bool |
|
}{ |
|
{ |
|
req: adminschema.ConnectorsSetRequest{ |
|
Connectors: []interface{}{ |
|
map[string]string{ |
|
"type": "local", |
|
"id": "local", |
|
}, |
|
}, |
|
}, |
|
want: adminschema.ConnectorsGetResponse{ |
|
Connectors: []interface{}{ |
|
map[string]string{ |
|
"id": "local", |
|
}, |
|
}, |
|
}, |
|
wantErr: false, |
|
}, |
|
{ |
|
req: adminschema.ConnectorsSetRequest{ |
|
Connectors: []interface{}{ |
|
map[string]string{ |
|
"type": "github", |
|
"id": "github", |
|
"clientID": "foo", |
|
"clientSecret": "bar", |
|
}, |
|
map[string]interface{}{ |
|
"type": "oidc", |
|
"id": "oidc", |
|
"issuerURL": "https://auth.example.com", |
|
"clientID": "foo", |
|
"clientSecret": "bar", |
|
"trustedEmailProvider": true, |
|
"emailClaim": "", |
|
}, |
|
}, |
|
}, |
|
want: adminschema.ConnectorsGetResponse{ |
|
Connectors: []interface{}{ |
|
map[string]string{ |
|
"id": "github", |
|
"clientID": "foo", |
|
"clientSecret": "bar", |
|
}, |
|
map[string]interface{}{ |
|
"id": "oidc", |
|
"issuerURL": "https://auth.example.com", |
|
"clientID": "foo", |
|
"clientSecret": "bar", |
|
"trustedEmailProvider": true, |
|
"emailClaim": "", |
|
}, |
|
}, |
|
}, |
|
wantErr: false, |
|
}, |
|
{ |
|
// Missing "type" argument |
|
req: adminschema.ConnectorsSetRequest{ |
|
Connectors: []interface{}{ |
|
map[string]string{ |
|
"id": "local", |
|
}, |
|
}, |
|
}, |
|
wantErr: true, |
|
}, |
|
} |
|
|
|
for i, tt := range tests { |
|
f := makeAdminAPITestFixtures() |
|
if err := f.adClient.Connectors.Set(&tt.req).Do(); err != nil { |
|
if !tt.wantErr { |
|
t.Errorf("case %d: failed to set connectors: %v", i, err) |
|
} |
|
continue |
|
} |
|
if tt.wantErr { |
|
t.Errorf("case %d: expected error setting connectors", i) |
|
continue |
|
} |
|
|
|
resp, err := f.adClient.Connectors.Get().Do() |
|
if err != nil { |
|
t.Errorf("case %d: failed toget connectors: %v", i, err) |
|
continue |
|
} |
|
if diff := pretty.Compare(tt.want, resp); diff != "" { |
|
t.Errorf("case %d: Compare(want, got) = %s", i, diff) |
|
} |
|
} |
|
} |
|
|
|
func TestCreateClient(t *testing.T) { |
|
mustParseURL := func(s string) *url.URL { |
|
u, err := url.Parse(s) |
|
if err != nil { |
|
t.Fatalf("couldn't parse URL: %v", err) |
|
} |
|
return u |
|
} |
|
|
|
addIDAndSecret := func(cli adminschema.Client) *adminschema.Client { |
|
if cli.Id == "" { |
|
if cli.Public { |
|
cli.Id = "client_" + cli.ClientName |
|
} else { |
|
cli.Id = "client_auth.example.com" |
|
} |
|
} |
|
|
|
if cli.Secret == "" { |
|
cli.Secret = base64.URLEncoding.EncodeToString([]byte("client_0")) |
|
} |
|
return &cli |
|
} |
|
|
|
adminClientGood := adminschema.Client{ |
|
RedirectURIs: []string{"https://auth.example.com/"}, |
|
} |
|
clientGood := client.Client{ |
|
Credentials: oidc.ClientCredentials{ |
|
ID: "client_auth.example.com", |
|
}, |
|
Metadata: oidc.ClientMetadata{ |
|
RedirectURIs: []url.URL{*mustParseURL("https://auth.example.com/")}, |
|
}, |
|
} |
|
|
|
clientPublicGood := clientGood |
|
clientPublicGood.Public = true |
|
clientPublicGood.Metadata.ClientName = "PublicName" |
|
clientPublicGood.Metadata.RedirectURIs = []url.URL{} |
|
clientPublicGood.Credentials.ID = "client_PublicName" |
|
|
|
adminPublicClientGood := adminClientGood |
|
adminPublicClientGood.Public = true |
|
adminPublicClientGood.ClientName = "PublicName" |
|
adminPublicClientGood.RedirectURIs = []string{} |
|
|
|
adminPublicClientMissingName := adminPublicClientGood |
|
adminPublicClientMissingName.ClientName = "" |
|
|
|
adminPublicClientHasARedirect := adminPublicClientGood |
|
adminPublicClientHasARedirect.RedirectURIs = []string{"https://auth.example.com/"} |
|
|
|
adminAdminClient := adminClientGood |
|
adminAdminClient.IsAdmin = true |
|
clientGoodAdmin := clientGood |
|
clientGoodAdmin.Admin = true |
|
|
|
adminMultiRedirect := adminClientGood |
|
adminMultiRedirect.RedirectURIs = []string{"https://auth.example.com/", "https://auth2.example.com/"} |
|
clientMultiRedirect := clientGood |
|
clientMultiRedirect.Metadata.RedirectURIs = append( |
|
clientMultiRedirect.Metadata.RedirectURIs, |
|
*mustParseURL("https://auth2.example.com/")) |
|
|
|
adminClientWithPeers := adminClientGood |
|
adminClientWithPeers.TrustedPeers = []string{"test_client_0"} |
|
|
|
adminClientOwnID := adminClientGood |
|
adminClientOwnID.Id = "my_own_id" |
|
|
|
clientGoodOwnID := clientGood |
|
clientGoodOwnID.Credentials.ID = "my_own_id" |
|
|
|
adminClientOwnSecret := adminClientGood |
|
adminClientOwnSecret.Secret = base64.URLEncoding.EncodeToString([]byte("my_own_secret")) |
|
clientGoodOwnSecret := clientGood |
|
|
|
adminClientOwnIDAndSecret := adminClientGood |
|
adminClientOwnIDAndSecret.Id = "my_own_id" |
|
adminClientOwnIDAndSecret.Secret = base64.URLEncoding.EncodeToString([]byte("my_own_secret")) |
|
clientGoodOwnIDAndSecret := clientGoodOwnID |
|
|
|
adminClientBadSecret := adminClientGood |
|
adminClientBadSecret.Secret = "not_base64_encoded" |
|
|
|
tests := []struct { |
|
req adminschema.ClientCreateRequest |
|
want adminschema.ClientCreateResponse |
|
wantClient client.Client |
|
wantError int |
|
wantTrustedPeers []string |
|
}{ |
|
{ |
|
req: adminschema.ClientCreateRequest{}, |
|
wantError: http.StatusBadRequest, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminschema.Client{ |
|
IsAdmin: true, |
|
}, |
|
}, |
|
wantError: http.StatusBadRequest, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminschema.Client{ |
|
RedirectURIs: []string{"909090"}, |
|
}, |
|
}, |
|
wantError: http.StatusBadRequest, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminClientGood, |
|
}, |
|
want: adminschema.ClientCreateResponse{ |
|
Client: addIDAndSecret(adminClientGood), |
|
}, |
|
wantClient: clientGood, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminAdminClient, |
|
}, |
|
want: adminschema.ClientCreateResponse{ |
|
Client: addIDAndSecret(adminAdminClient), |
|
}, |
|
wantClient: clientGoodAdmin, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminMultiRedirect, |
|
}, |
|
want: adminschema.ClientCreateResponse{ |
|
Client: addIDAndSecret(adminMultiRedirect), |
|
}, |
|
wantClient: clientMultiRedirect, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminClientWithPeers, |
|
}, |
|
want: adminschema.ClientCreateResponse{ |
|
Client: addIDAndSecret(adminClientWithPeers), |
|
}, |
|
wantClient: clientGood, |
|
wantTrustedPeers: []string{"test_client_0"}, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminPublicClientGood, |
|
}, |
|
want: adminschema.ClientCreateResponse{ |
|
Client: addIDAndSecret(adminPublicClientGood), |
|
}, |
|
wantClient: clientPublicGood, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminPublicClientMissingName, |
|
}, |
|
wantError: http.StatusBadRequest, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminPublicClientHasARedirect, |
|
}, |
|
wantError: http.StatusBadRequest, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminClientOwnID, |
|
}, |
|
want: adminschema.ClientCreateResponse{ |
|
Client: addIDAndSecret(adminClientOwnID), |
|
}, |
|
wantClient: clientGoodOwnID, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminClientOwnSecret, |
|
}, |
|
want: adminschema.ClientCreateResponse{ |
|
Client: addIDAndSecret(adminClientOwnSecret), |
|
}, |
|
wantClient: clientGoodOwnSecret, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminClientOwnIDAndSecret, |
|
}, |
|
want: adminschema.ClientCreateResponse{ |
|
Client: addIDAndSecret(adminClientOwnIDAndSecret), |
|
}, |
|
wantClient: clientGoodOwnIDAndSecret, |
|
}, { |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminClientBadSecret, |
|
}, |
|
wantError: http.StatusBadRequest, |
|
}, { |
|
// Client ID already exists |
|
req: adminschema.ClientCreateRequest{ |
|
Client: &adminschema.Client{ |
|
Id: "client-1", |
|
Secret: "Zm9vYmFy", |
|
RedirectURIs: []string{"https://auth.example.com/"}, |
|
}, |
|
}, |
|
wantError: http.StatusConflict, |
|
}, |
|
} |
|
|
|
for i, tt := range tests { |
|
f := makeAdminAPITestFixtures() |
|
for j, r := range []string{"https://client0.example.com", |
|
"https://client1.example.com"} { |
|
_, err := f.cr.New(nil, client.Client{ |
|
Credentials: oidc.ClientCredentials{ |
|
ID: fmt.Sprintf("test_client_%d", j), |
|
}, |
|
Metadata: oidc.ClientMetadata{ |
|
RedirectURIs: []url.URL{*mustParseURL(r)}, |
|
}, |
|
}) |
|
if err != nil { |
|
t.Errorf("case %d, client %d: unexpected error creating client: %v", i, j, err) |
|
continue |
|
} |
|
} |
|
|
|
resp, err := f.adClient.Client.Create(&tt.req).Do() |
|
if tt.wantError != 0 { |
|
if err == nil { |
|
t.Errorf("case %d: want non-nil error.", i) |
|
continue |
|
} |
|
|
|
aErr, ok := err.(*googleapi.Error) |
|
if !ok { |
|
t.Errorf("case %d: could not assert as adminSchema.Error: %v", i, err) |
|
continue |
|
} |
|
if aErr.Code != tt.wantError { |
|
t.Errorf("case %d: want aErr.Code=%v, got %v: %v", i, tt.wantError, aErr.Code, aErr) |
|
continue |
|
} |
|
continue |
|
} |
|
|
|
if err != nil { |
|
t.Errorf("case %d: unexpected error creating client: %v", i, err) |
|
continue |
|
} |
|
|
|
if diff := pretty.Compare(tt.want, resp); diff != "" { |
|
t.Errorf("case %d: Compare(want, got) = %v", i, diff) |
|
} |
|
|
|
repoClient, err := f.cr.Get(nil, resp.Client.Id) |
|
if err != nil { |
|
t.Errorf("case %d: Unexpected error getting client: %v", i, err) |
|
continue |
|
} |
|
|
|
if diff := pretty.Compare(tt.wantClient, repoClient); diff != "" { |
|
t.Errorf("case %d: Compare(wantClient, repoClient) = %v", i, diff) |
|
} |
|
} |
|
} |
|
|
|
func TestGetState(t *testing.T) { |
|
tests := []struct { |
|
addUsers []user.User |
|
want adminschema.State |
|
}{ |
|
{ |
|
addUsers: []user.User{ |
|
user.User{ |
|
ID: "ID-admin", |
|
Email: "Admin@example.com", |
|
Admin: true, |
|
}, |
|
}, |
|
want: adminschema.State{ |
|
AdminUserCreated: true, |
|
}, |
|
}, |
|
{ |
|
want: adminschema.State{ |
|
AdminUserCreated: false, |
|
}, |
|
}, |
|
} |
|
|
|
for i, tt := range tests { |
|
func() { |
|
f := makeAdminAPITestFixtures() |
|
defer f.close() |
|
|
|
for _, usr := range tt.addUsers { |
|
err := f.ur.Create(nil, usr) |
|
if err != nil { |
|
t.Fatalf("case %d: err != nil: %v", i, err) |
|
} |
|
} |
|
|
|
got, err := f.adClient.State.Get().Do() |
|
if err != nil { |
|
t.Errorf("case %d: err != nil: %q", i, err) |
|
} |
|
|
|
if diff := pretty.Compare(tt.want, got); diff != "" { |
|
t.Errorf("case %d: Compare(want, got) = %v", i, diff) |
|
} |
|
|
|
}() |
|
} |
|
|
|
}
|
|
|