Esempio n. 1
0
func TestServerTokenUnrecognizedKey(t *testing.T) {
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				validRedirURL,
			},
		},
	}

	clients := []client.Client{ci}
	dbm := db.NewMemDB()
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	km := &StaticKeyManager{
		signer: &StaticSigner{sig: []byte("beer"), err: nil},
	}
	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))

	srv := &Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		SessionManager: sm,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
	}

	sessionID, err := sm.NewSession("connector_id", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = sm.AttachRemoteIdentity(sessionID, oidc.Identity{})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	jwt, token, err := srv.CodeToken(ci.Credentials, "foo")
	if err == nil {
		t.Fatalf("Expected non-nil error")
	}
	if jwt != nil {
		t.Fatalf("Expected nil jwt")
	}
	if token != "" {
		t.Fatalf("Expected empty refresh token")
	}
}
Esempio n. 2
0
func makeTestFixtures() *testFixtures {
	f := &testFixtures{}

	dbMap := db.NewMemDB()
	clients := []client.Client{
		{
			Credentials: oidc.ClientCredentials{
				ID:     "client.example.com",
				Secret: goodSecret,
			},
			Metadata: oidc.ClientMetadata{
				RedirectURIs: []url.URL{
					{Scheme: "http", Host: "client.example.com", Path: "/"},
				},
			},
			Admin: true,
		},
	}
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	f.clientRepo = db.NewClientRepo(dbMap)
	clientManager, err := NewClientManagerFromClients(f.clientRepo, db.TransactionFactory(dbMap), clients, ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		panic("Failed to create client manager: " + err.Error())
	}
	f.mgr = clientManager
	return f
}
Esempio n. 3
0
func TestDBClientRepoMetadata(t *testing.T) {
	r := db.NewClientRepo(connect(t))

	cm := 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"},
		},
	}

	_, err := r.New(nil, client.Client{
		Credentials: oidc.ClientCredentials{
			ID: "foo",
		},
		Metadata: cm,
	})
	if err != nil {
		t.Fatalf(err.Error())
	}

	got, err := r.Get(nil, "foo")
	if err != nil {
		t.Fatalf(err.Error())
	}

	if diff := pretty.Compare(cm, got.Metadata); diff != "" {
		t.Fatalf("Retrieved incorrect ClientMetadata: Compare(want,got): %v", diff)
	}
}
Esempio n. 4
0
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
	}
	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
}
Esempio n. 5
0
func TestDBClientRepoNewDuplicate(t *testing.T) {
	r := db.NewClientRepo(connect(t))

	meta1 := oidc.ClientMetadata{
		RedirectURIs: []url.URL{
			url.URL{Scheme: "http", Host: "foo.example.com"},
		},
	}

	if _, err := r.New(nil, client.Client{
		Credentials: oidc.ClientCredentials{
			ID: "foo",
		},
		Metadata: meta1,
	}); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	meta2 := oidc.ClientMetadata{
		RedirectURIs: []url.URL{
			url.URL{Scheme: "http", Host: "bar.example.com"},
		},
	}

	if _, err := r.New(nil, client.Client{
		Credentials: oidc.ClientCredentials{
			ID: "foo",
		},
		Metadata: meta2,
	}); err == nil {
		t.Fatalf("expected non-nil error")
	}
}
Esempio n. 6
0
func TestDBClientRepoAuthenticate(t *testing.T) {
	c := connect(t)
	r := db.NewClientRepo(c)

	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	m := manager.NewClientManager(r, db.TransactionFactory(c), manager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})

	cm := oidc.ClientMetadata{
		RedirectURIs: []url.URL{
			url.URL{Scheme: "http", Host: "127.0.0.1:5556", Path: "/cb"},
		},
	}
	cli := client.Client{
		Metadata: cm,
	}
	cc, err := m.New(cli, nil)
	if err != nil {
		t.Fatalf(err.Error())
	}

	if cc.ID != "127.0.0.1:5556" {
		t.Fatalf("Returned ClientCredentials has incorrect ID: want=baz got=%s", cc.ID)
	}

	ok, err := m.Authenticate(*cc)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	} else if !ok {
		t.Fatalf("Authentication failed for good creds")
	}

	creds := []oidc.ClientCredentials{
		// completely made up
		oidc.ClientCredentials{ID: "foo", Secret: "bar"},

		// good client ID, bad secret
		oidc.ClientCredentials{ID: cc.ID, Secret: "bar"},

		// bad client ID, good secret
		oidc.ClientCredentials{ID: "foo", Secret: cc.Secret},

		// good client ID, secret with some fluff on the end
		oidc.ClientCredentials{ID: cc.ID, Secret: fmt.Sprintf("%sfluff", cc.Secret)},
	}
	for i, c := range creds {
		ok, err := m.Authenticate(c)
		if err != nil {
			t.Errorf("case %d: unexpected error: %v", i, err)
		} else if ok {
			t.Errorf("case %d: authentication succeeded for bad creds", i)
		}
	}
}
Esempio n. 7
0
func TestDBClientAll(t *testing.T) {
	r := db.NewClientRepo(connect(t))

	cm := oidc.ClientMetadata{
		RedirectURIs: []url.URL{
			url.URL{Scheme: "http", Host: "127.0.0.1:5556", Path: "/cb"},
		},
	}

	_, err := r.New(nil, client.Client{
		Credentials: oidc.ClientCredentials{
			ID: "foo",
		},
		Metadata: cm,
	})
	if err != nil {
		t.Fatalf(err.Error())
	}

	got, err := r.All(nil)
	if err != nil {
		t.Fatalf(err.Error())
	}
	count := len(got)
	if count != 1 {
		t.Fatalf("Retrieved incorrect number of ClientIdentities: want=1 got=%d", count)
	}

	if diff := pretty.Compare(cm, got[0].Metadata); diff != "" {
		t.Fatalf("Retrieved incorrect ClientMetadata: Compare(want,got): %v", diff)
	}

	cm = oidc.ClientMetadata{
		RedirectURIs: []url.URL{
			url.URL{Scheme: "http", Host: "foo.com", Path: "/cb"},
		},
	}
	_, err = r.New(nil, client.Client{
		Credentials: oidc.ClientCredentials{
			ID: "bar",
		},
		Metadata: cm,
	})
	if err != nil {
		t.Fatalf(err.Error())
	}

	got, err = r.All(nil)
	if err != nil {
		t.Fatalf(err.Error())
	}
	count = len(got)
	if count != 2 {
		t.Fatalf("Retrieved incorrect number of ClientIdentities: want=2 got=%d", count)
	}
}
Esempio n. 8
0
func TestCreate(t *testing.T) {
	dbm := db.NewMemDB()
	repo := db.NewClientRepo(dbm)
	manager := manager.NewClientManager(repo, db.TransactionFactory(dbm), manager.ManagerOptions{})
	res := &clientResource{manager: manager}
	tests := [][]string{
		[]string{"http://example.com"},
		[]string{"https://example.com"},
		[]string{"http://example.com/foo"},
		[]string{"http://example.com/bar", "http://example.com/foo"},
	}
	endpoint := "http://example.com/clients"

	for i, tt := range tests {
		body := strings.NewReader(fmt.Sprintf(`{"redirectURIs":["%s"]}`, strings.Join(tt, `","`)))
		r, err := http.NewRequest("POST", endpoint, body)
		if err != nil {
			t.Fatalf("Failed creating http.Request: %v", err)
		}
		r.Header.Set("content-type", "application/json")
		w := httptest.NewRecorder()
		res.ServeHTTP(w, r)

		if w.Code != http.StatusCreated {
			t.Errorf("case %d: invalid response code, want=%d, got=%d", i, http.StatusCreated, w.Code)
		}

		var client schema.ClientWithSecret
		if err := json.Unmarshal(w.Body.Bytes(), &client); err != nil {
			t.Errorf("case %d: unexpected error=%v", i, err)
		}
		if len(client.RedirectURIs) != len(tt) {
			t.Errorf("case %d: unexpected number of redirect URIs, want=%d, got=%d", i, len(tt), len(client.RedirectURIs))
		}

		if !reflect.DeepEqual(tt, client.RedirectURIs) {
			t.Errorf("case %d: unexpected client redirect URIs: want=%v got=%v", i, tt, client.RedirectURIs)
		}

		if client.Id == "" {
			t.Errorf("case %d: empty client ID in response", i)
		}

		if client.Secret == "" {
			t.Errorf("case %d: empty client secret in response", i)
		}

		wantLoc := fmt.Sprintf("%s/%s", endpoint, client.Id)
		gotLoc := w.Header().Get("Location")
		if gotLoc != wantLoc {
			t.Errorf("case %d: invalid location header, want=%v, got=%v", i, wantLoc, gotLoc)
		}
	}
}
Esempio n. 9
0
func newDBConnector(dsn string) (*dbConnector, error) {
	dbc, err := db.NewConnection(db.Config{DSN: dsn})
	if err != nil {
		return nil, err
	}

	dConn := &dbConnector{
		cfgRepo:   db.NewConnectorConfigRepo(dbc),
		ciManager: manager.NewClientManager(db.NewClientRepo(dbc), db.TransactionFactory(dbc), manager.ManagerOptions{}),
	}

	return dConn, nil
}
Esempio n. 10
0
func TestDBClientRepoMetadataNoExist(t *testing.T) {
	c := connect(t)
	r := db.NewClientRepo(c)
	m := manager.NewClientManager(r, db.TransactionFactory(c), manager.ManagerOptions{})

	got, err := m.Metadata("noexist")
	if err != client.ErrorNotFound {
		t.Errorf("want==%q, got==%q", client.ErrorNotFound, err)
	}
	if got != nil {
		t.Fatalf("Retrieved incorrect ClientMetadata: want=nil got=%#v", got)
	}
}
Esempio n. 11
0
func newRefreshRepo(t *testing.T, users []user.UserWithRemoteIdentities, clients []client.Client) refresh.RefreshTokenRepo {
	var dbMap *gorp.DbMap
	if dsn := os.Getenv("DEX_TEST_DSN"); dsn == "" {
		dbMap = db.NewMemDB()
	} else {
		dbMap = connect(t)
	}
	if _, err := db.NewUserRepoFromUsers(dbMap, users); err != nil {
		t.Fatalf("Unable to add users: %v", err)
	}
	if _, err := manager.NewClientManagerFromClients(db.NewClientRepo(dbMap), db.TransactionFactory(dbMap), clients, manager.ManagerOptions{}); err != nil {
		t.Fatalf("Unable to add clients: %v", err)
	}
	return db.NewRefreshTokenRepo(dbMap)
}
Esempio n. 12
0
func TestServerLoginUnrecognizedSessionKey(t *testing.T) {
	clients := []client.Client{
		client.Client{
			Credentials: oidc.ClientCredentials{
				ID: testClientID, Secret: clientTestSecret,
			},
			Metadata: oidc.ClientMetadata{
				RedirectURIs: []url.URL{
					validRedirURL,
				},
			},
		},
	}
	dbm := db.NewMemDB()
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	km := &StaticKeyManager{
		signer: &StaticSigner{sig: nil, err: errors.New("fail")},
	}
	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
	srv := &Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		SessionManager: sm,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
	}

	ident := oidc.Identity{ID: "YYY", Name: "elroy", Email: "*****@*****.**"}
	code, err := srv.Login(ident, testClientID)
	if err == nil {
		t.Fatalf("Expected non-nil error")
	}

	if code != "" {
		t.Fatalf("Expected empty code, got=%s", code)
	}
}
Esempio n. 13
0
func (cfg *MultiServerConfig) Configure(srv *Server) error {
	if len(cfg.KeySecrets) == 0 {
		return errors.New("missing key secret")
	}

	if cfg.DatabaseConfig.DSN == "" {
		return errors.New("missing database connection string")
	}

	dbc, err := db.NewConnection(cfg.DatabaseConfig)
	if err != nil {
		return fmt.Errorf("unable to initialize database connection: %v", err)
	}
	if _, ok := dbc.Dialect.(gorp.PostgresDialect); !ok {
		return errors.New("only postgres backend supported for multi server configurations")
	}

	kRepo, err := db.NewPrivateKeySetRepo(dbc, cfg.UseOldFormat, cfg.KeySecrets...)
	if err != nil {
		return fmt.Errorf("unable to create PrivateKeySetRepo: %v", err)
	}

	ciRepo := db.NewClientRepo(dbc)
	sRepo := db.NewSessionRepo(dbc)
	skRepo := db.NewSessionKeyRepo(dbc)
	cfgRepo := db.NewConnectorConfigRepo(dbc)
	userRepo := db.NewUserRepo(dbc)
	pwiRepo := db.NewPasswordInfoRepo(dbc)
	userManager := usermanager.NewUserManager(userRepo, pwiRepo, cfgRepo, db.TransactionFactory(dbc), usermanager.ManagerOptions{})
	clientManager := clientmanager.NewClientManager(ciRepo, db.TransactionFactory(dbc), clientmanager.ManagerOptions{})
	refreshTokenRepo := db.NewRefreshTokenRepo(dbc)

	sm := sessionmanager.NewSessionManager(sRepo, skRepo)

	srv.ClientRepo = ciRepo
	srv.ClientManager = clientManager
	srv.KeySetRepo = kRepo
	srv.ConnectorConfigRepo = cfgRepo
	srv.UserRepo = userRepo
	srv.UserManager = userManager
	srv.PasswordInfoRepo = pwiRepo
	srv.SessionManager = sm
	srv.RefreshTokenRepo = refreshTokenRepo
	srv.HealthChecks = append(srv.HealthChecks, db.NewHealthChecker(dbc))
	srv.dbMap = dbc
	return nil
}
Esempio n. 14
0
func TestDBClientRepoNewAdmin(t *testing.T) {

	for _, admin := range []bool{true, false} {
		r := db.NewClientRepo(connect(t))
		if _, err := r.New(nil, client.Client{
			Credentials: oidc.ClientCredentials{
				ID: "foo",
			},
			Metadata: oidc.ClientMetadata{
				RedirectURIs: []url.URL{
					url.URL{Scheme: "http", Host: "foo.example.com"},
				},
			},
			Admin: admin,
		}); err != nil {
			t.Fatalf("expected non-nil error: %v", err)
		}

		gotAdmin, err := r.Get(nil, "foo")
		if err != nil {
			t.Fatalf("expected non-nil error")
		}
		if gotAdmin.Admin != admin {
			t.Errorf("want=%v, gotAdmin=%v", admin, gotAdmin)
		}

		cli, err := r.Get(nil, "foo")
		if err != nil {
			t.Fatalf("expected non-nil error")
		}
		if cli.Admin != admin {
			t.Errorf("want=%v, cli.Admin=%v", admin, cli.Admin)
		}
	}

}
Esempio n. 15
0
func mockServer(cis []client.Client) (*server.Server, error) {
	dbMap := db.NewMemDB()
	k, err := key.GeneratePrivateKey()
	if err != nil {
		return nil, fmt.Errorf("Unable to generate private key: %v", err)
	}

	km := key.NewPrivateKeyManager()
	err = km.Set(key.NewPrivateKeySet([]*key.PrivateKey{k}, time.Now().Add(time.Minute)))
	if err != nil {
		return nil, err
	}

	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbMap)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbMap), cis, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		return nil, err
	}

	sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap))
	srv := &server.Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
		SessionManager: sm,
	}

	return srv, nil
}
Esempio n. 16
0
func makeTestFixtures() (*UsersAPI, *testEmailer) {
	dbMap := db.NewMemDB()
	ur := func() user.UserRepo {
		repo, err := db.NewUserRepoFromUsers(dbMap, []user.UserWithRemoteIdentities{
			{
				User: user.User{
					ID:        "ID-1",
					Email:     "*****@*****.**",
					Admin:     true,
					CreatedAt: clock.Now(),
				},
			}, {
				User: user.User{
					ID:            "ID-2",
					Email:         "*****@*****.**",
					EmailVerified: true,
					CreatedAt:     clock.Now(),
				},
			}, {
				User: user.User{
					ID:        "ID-3",
					Email:     "*****@*****.**",
					CreatedAt: clock.Now(),
				},
			}, {
				User: user.User{
					ID:        "ID-4",
					Email:     "*****@*****.**",
					CreatedAt: clock.Now(),
					Disabled:  true,
				},
			},
		})
		if err != nil {
			panic("Failed to create user repo: " + err.Error())
		}
		return repo
	}()

	pwr := func() user.PasswordInfoRepo {
		repo, err := db.NewPasswordInfoRepoFromPasswordInfos(dbMap, []user.PasswordInfo{
			{
				UserID:   "ID-1",
				Password: []byte("password-1"),
			},
			{
				UserID:   "ID-2",
				Password: []byte("password-2"),
			},
		})
		if err != nil {
			panic("Failed to create user repo: " + err.Error())
		}
		return repo
	}()

	ccr := func() connector.ConnectorConfigRepo {
		repo := db.NewConnectorConfigRepo(dbMap)
		c := []connector.ConnectorConfig{
			&connector.LocalConnectorConfig{ID: "local"},
		}
		if err := repo.Set(c); err != nil {
			panic(err)
		}
		return repo
	}()

	mgr := manager.NewUserManager(ur, pwr, ccr, db.TransactionFactory(dbMap), manager.ManagerOptions{})
	mgr.Clock = clock
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     goodClientID,
			Secret: base64.URLEncoding.EncodeToString([]byte("secret")),
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				validRedirURL,
			},
		},
	}

	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbMap)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbMap), []client.Client{ci}, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		panic("Failed to create client manager: " + err.Error())
	}

	// Used in TestRevokeRefreshToken test.
	refreshTokens := []struct {
		clientID string
		userID   string
	}{
		{goodClientID, "ID-1"},
		{goodClientID, "ID-2"},
	}
	refreshRepo := db.NewRefreshTokenRepo(dbMap)
	for _, token := range refreshTokens {
		if _, err := refreshRepo.Create(token.userID, token.clientID); err != nil {
			panic("Failed to create refresh token: " + err.Error())
		}
	}

	emailer := &testEmailer{}
	api := NewUsersAPI(mgr, clientManager, refreshRepo, emailer, "local")
	return api, emailer

}
Esempio n. 17
0
func makeTestFixtures() (*testFixtures, error) {
	dbMap := db.NewMemDB()
	userRepo, err := db.NewUserRepoFromUsers(dbMap, testUsers)
	if err != nil {
		return nil, err
	}
	pwRepo, err := db.NewPasswordInfoRepoFromPasswordInfos(dbMap, testPasswordInfos)
	if err != nil {
		return nil, err
	}

	connConfigs := []connector.ConnectorConfig{
		&connector.OIDCConnectorConfig{
			ID:           "oidc",
			IssuerURL:    testIssuerURL.String(),
			ClientID:     "12345",
			ClientSecret: "567789",
		},
		&connector.OIDCConnectorConfig{
			ID:                   "oidc-trusted",
			IssuerURL:            testIssuerURL.String(),
			ClientID:             "12345-trusted",
			ClientSecret:         "567789-trusted",
			TrustedEmailProvider: true,
		},
		&connector.LocalConnectorConfig{
			ID: "local",
		},
	}
	connCfgRepo := db.NewConnectorConfigRepo(dbMap)
	if err := connCfgRepo.Set(connConfigs); err != nil {
		return nil, err
	}

	userManager := usermanager.NewUserManager(userRepo, pwRepo, connCfgRepo, db.TransactionFactory(dbMap), usermanager.ManagerOptions{})

	sessionManager := sessionmanager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
	sessionManager.GenerateCode = sequentialGenerateCodeFunc()

	emailer, err := email.NewTemplatizedEmailerFromGlobs(
		emailTemplatesLocation+"/*.txt",
		emailTemplatesLocation+"/*.html",
		&email.FakeEmailer{})
	if err != nil {
		return nil, err
	}

	clients := []client.Client{
		client.Client{
			Credentials: oidc.ClientCredentials{
				ID:     testClientID,
				Secret: base64.URLEncoding.EncodeToString([]byte("secret")),
			},
			Metadata: oidc.ClientMetadata{
				RedirectURIs: []url.URL{
					testRedirectURL,
				},
			},
		},
	}

	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbMap)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbMap), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		return nil, err
	}
	km := key.NewPrivateKeyManager()
	err = km.Set(key.NewPrivateKeySet([]*key.PrivateKey{testPrivKey}, time.Now().Add(time.Minute)))
	if err != nil {
		return nil, err
	}

	tpl, err := getTemplates("dex",
		"https://coreos.com/assets/images/brand/coreos-mark-30px.png",
		true, templatesLocation)
	if err != nil {
		return nil, err
	}

	srv := &Server{
		IssuerURL:        testIssuerURL,
		SessionManager:   sessionManager,
		ClientRepo:       clientRepo,
		Templates:        tpl,
		UserRepo:         userRepo,
		PasswordInfoRepo: pwRepo,
		UserManager:      userManager,
		ClientManager:    clientManager,
		KeyManager:       km,
	}

	err = setTemplates(srv, tpl)
	if err != nil {
		return nil, err
	}

	for _, config := range connConfigs {
		if err := srv.AddConnector(config); err != nil {
			return nil, err
		}
	}

	srv.UserEmailer = useremail.NewUserEmailer(srv.UserRepo,
		srv.PasswordInfoRepo,
		srv.KeyManager.Signer,
		srv.SessionManager.ValidityWindow,
		srv.IssuerURL,
		emailer,
		"*****@*****.**",
		srv.absURL(httpPathResetPassword),
		srv.absURL(httpPathEmailVerify),
		srv.absURL(httpPathAcceptInvitation),
	)

	return &testFixtures{
		srv:            srv,
		redirectURL:    testRedirectURL,
		userRepo:       userRepo,
		sessionManager: sessionManager,
		emailer:        emailer,
		clientRepo:     clientRepo,
		clientManager:  clientManager,
	}, nil
}
Esempio n. 18
0
func (cfg *SingleServerConfig) Configure(srv *Server) error {
	k, err := key.GeneratePrivateKey()
	if err != nil {
		return err
	}

	dbMap := db.NewMemDB()

	ks := key.NewPrivateKeySet([]*key.PrivateKey{k}, time.Now().Add(24*time.Hour))
	kRepo := key.NewPrivateKeySetRepo()
	if err = kRepo.Set(ks); err != nil {
		return err
	}

	clients, err := loadClients(cfg.ClientsFile)
	if err != nil {
		return fmt.Errorf("unable to read clients from file %s: %v", cfg.ClientsFile, err)
	}

	clientRepo := db.NewClientRepo(dbMap)

	for _, c := range clients {
		clientRepo.New(nil, c)
	}

	f, err := os.Open(cfg.ConnectorsFile)
	if err != nil {
		return fmt.Errorf("opening connectors file: %v", err)
	}
	defer f.Close()
	cfgs, err := connector.ReadConfigs(f)
	if err != nil {
		return fmt.Errorf("decoding connector configs: %v", err)
	}
	cfgRepo := db.NewConnectorConfigRepo(dbMap)
	if err := cfgRepo.Set(cfgs); err != nil {
		return fmt.Errorf("failed to set connectors: %v", err)
	}

	sRepo := db.NewSessionRepo(dbMap)
	skRepo := db.NewSessionKeyRepo(dbMap)
	sm := sessionmanager.NewSessionManager(sRepo, skRepo)

	users, pwis, err := loadUsers(cfg.UsersFile)
	if err != nil {
		return fmt.Errorf("unable to read users from file: %v", err)
	}
	userRepo, err := db.NewUserRepoFromUsers(dbMap, users)
	if err != nil {
		return err
	}

	pwiRepo, err := db.NewPasswordInfoRepoFromPasswordInfos(dbMap, pwis)
	if err != nil {
		return err
	}

	refTokRepo := db.NewRefreshTokenRepo(dbMap)

	txnFactory := db.TransactionFactory(dbMap)
	userManager := usermanager.NewUserManager(userRepo, pwiRepo, cfgRepo, txnFactory, usermanager.ManagerOptions{})
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbMap), clients, clientmanager.ManagerOptions{})
	if err != nil {
		return fmt.Errorf("Failed to create client identity manager: %v", err)
	}
	srv.ClientRepo = clientRepo
	srv.ClientManager = clientManager
	srv.KeySetRepo = kRepo
	srv.ConnectorConfigRepo = cfgRepo
	srv.UserRepo = userRepo
	srv.UserManager = userManager
	srv.PasswordInfoRepo = pwiRepo
	srv.SessionManager = sm
	srv.RefreshTokenRepo = refTokRepo
	srv.HealthChecks = append(srv.HealthChecks, db.NewHealthChecker(dbMap))
	srv.dbMap = dbMap
	return nil
}
Esempio n. 19
0
func TestList(t *testing.T) {

	b64Encode := func(s string) string {
		return base64.URLEncoding.EncodeToString([]byte(s))
	}

	tests := []struct {
		cs   []client.Client
		want []*schema.Client
	}{
		// empty repo
		{
			cs:   nil,
			want: nil,
		},
		// single client
		{
			cs: []client.Client{
				client.Client{
					Credentials: oidc.ClientCredentials{ID: "example.com", Secret: b64Encode("secret")},
					Metadata: oidc.ClientMetadata{
						RedirectURIs: []url.URL{
							url.URL{Scheme: "http", Host: "example.com"},
						},
					},
				},
			},
			want: []*schema.Client{
				&schema.Client{
					Id:           "example.com",
					RedirectURIs: []string{"http://example.com"},
				},
			},
		},
		// multi client
		{
			cs: []client.Client{
				client.Client{
					Credentials: oidc.ClientCredentials{ID: "example.com", Secret: b64Encode("secret")},
					Metadata: oidc.ClientMetadata{
						RedirectURIs: []url.URL{
							url.URL{Scheme: "http", Host: "example.com"},
						},
					},
				},
				client.Client{
					Credentials: oidc.ClientCredentials{ID: "example2.com", Secret: b64Encode("secret")},
					Metadata: oidc.ClientMetadata{
						RedirectURIs: []url.URL{
							url.URL{Scheme: "https", Host: "example2.com", Path: "one/two/three"},
						},
					},
				},
			},
			want: []*schema.Client{
				&schema.Client{
					Id:           "example2.com",
					RedirectURIs: []string{"https://example2.com/one/two/three"},
				},
				&schema.Client{
					Id:           "example.com",
					RedirectURIs: []string{"http://example.com"},
				},
			},
		},
	}

	for i, tt := range tests {
		dbm := db.NewMemDB()
		clientIDGenerator := func(hostport string) (string, error) {
			return hostport, nil
		}
		secGen := func() ([]byte, error) {
			return []byte("secret"), nil
		}
		clientRepo := db.NewClientRepo(dbm)
		clientManager, err := manager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), tt.cs, manager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
		if err != nil {
			t.Fatalf("Failed to create client identity manager: %v", err)
			continue
		}
		res := &clientResource{manager: clientManager}

		r, err := http.NewRequest("GET", "http://example.com/clients", nil)
		if err != nil {
			t.Fatalf("Failed creating http.Request: %v", err)
		}
		w := httptest.NewRecorder()
		res.ServeHTTP(w, r)

		if w.Code != http.StatusOK {
			t.Errorf("case %d: invalid response code, want=%d, got=%d", i, http.StatusOK, w.Code)
		}

		var resp schema.ClientPage
		if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
			t.Errorf("case %d: unexpected error=%v", i, err)
		}
		sort.Sort(byClientId(tt.want))
		sort.Sort(byClientId(resp.Clients))

		if diff := pretty.Compare(tt.want, resp.Clients); diff != "" {
			t.Errorf("case %d: invalid response body: %s", i, diff)
		}
	}
}
Esempio n. 20
0
func TestCreateInvalidRequest(t *testing.T) {
	u := &url.URL{Scheme: "http", Host: "example.com", Path: "clients"}
	h := http.Header{"Content-Type": []string{"application/json"}}
	dbm := db.NewMemDB()
	repo := db.NewClientRepo(dbm)
	manager := manager.NewClientManager(repo, db.TransactionFactory(dbm), manager.ManagerOptions{})
	res := &clientResource{manager: manager}
	tests := []struct {
		req      *http.Request
		wantCode int
		wantBody string
	}{
		// invalid content-type
		{
			req:      &http.Request{Method: "POST", URL: u, Header: http.Header{"Content-Type": []string{"application/xml"}}},
			wantCode: http.StatusBadRequest,
			wantBody: `{"error":"invalid_request","error_description":"unsupported content-type"}`,
		},
		// invalid method
		{
			req:      &http.Request{Method: "DELETE", URL: u, Header: h},
			wantCode: http.StatusMethodNotAllowed,
			wantBody: `{"error":"invalid_request","error_description":"HTTP DELETE method not supported for this resource"}`,
		},
		// invalid method
		{
			req:      &http.Request{Method: "PUT", URL: u, Header: h},
			wantCode: http.StatusMethodNotAllowed,
			wantBody: `{"error":"invalid_request","error_description":"HTTP PUT method not supported for this resource"}`,
		},
		// invalid method
		{
			req:      &http.Request{Method: "HEAD", URL: u, Header: h},
			wantCode: http.StatusMethodNotAllowed,
			wantBody: `{"error":"invalid_request","error_description":"HTTP HEAD method not supported for this resource"}`,
		},
		// unserializable body
		{
			req:      &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody("asdf")},
			wantCode: http.StatusBadRequest,
			wantBody: `{"error":"invalid_request","error_description":"unable to decode request body"}`,
		},
		// empty body
		{
			req:      &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody("")},
			wantCode: http.StatusBadRequest,
			wantBody: `{"error":"invalid_request","error_description":"unable to decode request body"}`,
		},
		// missing url field
		{
			req:      &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"id":"foo"}`)},
			wantCode: http.StatusBadRequest,
			wantBody: `{"error":"invalid_client_metadata","error_description":"zero redirect URLs"}`,
		},
		// empty url array
		{
			req:      &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":[]}`)},
			wantCode: http.StatusBadRequest,
			wantBody: `{"error":"invalid_client_metadata","error_description":"zero redirect URLs"}`,
		},
		// array with empty string
		{
			req:      &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":[""]}`)},
			wantCode: http.StatusBadRequest,
			wantBody: `{"error":"invalid_client_metadata","error_description":"missing or invalid field: redirectURIs"}`,
		},
		// uri with unusable scheme
		{
			req:      &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":["asdf.com"]}`)},
			wantCode: http.StatusBadRequest,
			wantBody: `{"error":"invalid_client_metadata","error_description":"no host for uri field redirect_uris"}`,
		},
		// uri missing host
		{
			req:      &http.Request{Method: "POST", URL: u, Header: h, Body: makeBody(`{"redirectURIs":["http://"]}`)},
			wantCode: http.StatusBadRequest,
			wantBody: `{"error":"invalid_client_metadata","error_description":"no host for uri field redirect_uris"}`,
		},
	}

	for i, tt := range tests {
		w := httptest.NewRecorder()
		res.ServeHTTP(w, tt.req)

		if w.Code != tt.wantCode {
			t.Errorf("case %d: invalid response code, want=%d, got=%d", i, tt.wantCode, w.Code)
		}

		gotBody := w.Body.String()
		if gotBody != tt.wantBody {
			t.Errorf("case %d: invalid response body, want=%s, got=%s", i, tt.wantBody, gotBody)
		}
	}
}
Esempio n. 21
0
func makeUserAPITestFixtures() *userAPITestFixtures {
	f := &userAPITestFixtures{}

	dbMap, _, _, um := makeUserObjects(userUsers, userPasswords)
	clients := []client.Client{
		client.Client{
			Credentials: oidc.ClientCredentials{
				ID:     testClientID,
				Secret: testClientSecret,
			},
			Metadata: oidc.ClientMetadata{
				RedirectURIs: []url.URL{
					testRedirectURL,
				},
			},
		},
		client.Client{
			Credentials: oidc.ClientCredentials{
				ID:     userBadClientID,
				Secret: base64.URLEncoding.EncodeToString([]byte("secret")),
			},
			Metadata: oidc.ClientMetadata{
				RedirectURIs: []url.URL{
					testBadRedirectURL,
				},
			},
		},
	}
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte(testClientSecret), nil
	}
	clientRepo := db.NewClientRepo(dbMap)
	clientManager, err := manager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbMap), clients, manager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		panic("Failed to create client identity manager: " + err.Error())
	}
	clientManager.SetDexAdmin(testClientID, true)

	noop := func() error { return nil }

	keysFunc := func() []key.PublicKey {
		return []key.PublicKey{*key.NewPublicKey(testPrivKey.JWK())}
	}

	jwtvFactory := func(clientID string) oidc.JWTVerifier {
		return oidc.NewJWTVerifier(testIssuerURL.String(), clientID, noop, keysFunc)
	}

	refreshRepo := db.NewRefreshTokenRepo(dbMap)
	for _, user := range userUsers {
		if _, err := refreshRepo.Create(user.User.ID, testClientID); err != nil {
			panic("Failed to create refresh token: " + err.Error())
		}
	}

	f.emailer = &testEmailer{}
	um.Clock = clock

	api := api.NewUsersAPI(um, clientManager, refreshRepo, f.emailer, "local")
	usrSrv := server.NewUserMgmtServer(api, jwtvFactory, um, clientManager)
	f.hSrv = httptest.NewServer(usrSrv.HTTPHandler())

	f.trans = &tokenHandlerTransport{
		Handler: usrSrv.HTTPHandler(),
		Token:   userGoodToken,
	}
	hc := &http.Client{
		Transport: f.trans,
	}
	f.client, _ = schema.NewWithBasePath(hc, f.hSrv.URL)

	return f
}
Esempio n. 22
0
func TestServerRefreshToken(t *testing.T) {
	issuerURL := url.URL{Scheme: "http", Host: "server.example.com"}
	clientA := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				url.URL{Scheme: "https", Host: "client.example.com", Path: "one/two/three"},
			},
		},
	}
	clientB := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     "example2.com",
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				url.URL{Scheme: "https", Host: "example2.com", Path: "one/two/three"},
			},
		},
	}

	signerFixture := &StaticSigner{sig: []byte("beer"), err: nil}

	// NOTE(ericchiang): These tests assume that the database ID of the first
	// refresh token will be "1".
	tests := []struct {
		token    string
		clientID string // The client that associates with the token.
		creds    oidc.ClientCredentials
		signer   jose.Signer
		err      error
	}{
		// Everything is good.
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			clientA.Credentials,
			signerFixture,
			nil,
		},
		// Invalid refresh token(malformatted).
		{
			"invalid-token",
			clientA.Credentials.ID,
			clientA.Credentials,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidRequest),
		},
		// Invalid refresh token(invalid payload content).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-2"))),
			clientA.Credentials.ID,
			clientA.Credentials,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidRequest),
		},
		// Invalid refresh token(invalid ID content).
		{
			fmt.Sprintf("0/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			clientA.Credentials,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidRequest),
		},
		// Invalid client(client is not associated with the token).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			clientB.Credentials,
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(no client ID).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			oidc.ClientCredentials{ID: "", Secret: "aaa"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(no such client).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			oidc.ClientCredentials{ID: "AAA", Secret: "aaa"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(no secrets).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			oidc.ClientCredentials{ID: testClientID},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Invalid client(invalid secret).
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			oidc.ClientCredentials{ID: "bad-id", Secret: "bad-secret"},
			signerFixture,
			oauth2.NewError(oauth2.ErrorInvalidClient),
		},
		// Signing operation fails.
		{
			fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
			clientA.Credentials.ID,
			clientA.Credentials,
			&StaticSigner{sig: nil, err: errors.New("fail")},
			oauth2.NewError(oauth2.ErrorServerError),
		},
	}

	for i, tt := range tests {
		km := &StaticKeyManager{
			signer: tt.signer,
		}

		clients := []client.Client{
			clientA,
			clientB,
		}

		clientIDGenerator := func(hostport string) (string, error) {
			return hostport, nil
		}
		secGen := func() ([]byte, error) {
			return []byte("secret"), nil
		}
		dbm := db.NewMemDB()
		clientRepo := db.NewClientRepo(dbm)
		clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
		if err != nil {
			t.Fatalf("Failed to create client identity manager: %v", err)
		}
		userRepo, err := makeNewUserRepo()
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

		srv := &Server{
			IssuerURL:        issuerURL,
			KeyManager:       km,
			ClientRepo:       clientRepo,
			ClientManager:    clientManager,
			UserRepo:         userRepo,
			RefreshTokenRepo: refreshTokenRepo,
		}

		if _, err := refreshTokenRepo.Create("testid-1", tt.clientID); err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		jwt, err := srv.RefreshToken(tt.creds, tt.token)
		if !reflect.DeepEqual(err, tt.err) {
			t.Errorf("Case %d: expect: %v, got: %v", i, tt.err, err)
		}

		if jwt != nil {
			if string(jwt.Signature) != "beer" {
				t.Errorf("Case %d: expect signature: beer, got signature: %v", i, jwt.Signature)
			}
			claims, err := jwt.Claims()
			if err != nil {
				t.Errorf("Case %d: unexpected error: %v", i, err)
			}
			if claims["iss"] != issuerURL.String() || claims["sub"] != "testid-1" || claims["aud"] != testClientID {
				t.Errorf("Case %d: invalid claims: %v", i, claims)
			}
		}
	}

	// Test that we should return error when user cannot be found after
	// verifying the token.
	km := &StaticKeyManager{
		signer: signerFixture,
	}

	clients := []client.Client{
		clientA,
		clientB,
	}
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	dbm := db.NewMemDB()
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	userRepo, err := makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Create a user that will be removed later.
	if err := userRepo.Create(nil, user.User{
		ID:    "testid-2",
		Email: "*****@*****.**",
	}); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

	srv := &Server{
		IssuerURL:        issuerURL,
		KeyManager:       km,
		ClientRepo:       clientRepo,
		ClientManager:    clientManager,
		UserRepo:         userRepo,
		RefreshTokenRepo: refreshTokenRepo,
	}

	if _, err := refreshTokenRepo.Create("testid-2", clientA.Credentials.ID); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	// Recreate the user repo to remove the user we created.
	userRepo, err = makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	srv.UserRepo = userRepo

	_, err = srv.RefreshToken(clientA.Credentials, fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))))
	if !reflect.DeepEqual(err, oauth2.NewError(oauth2.ErrorServerError)) {
		t.Errorf("Expect: %v, got: %v", oauth2.NewError(oauth2.ErrorServerError), err)
	}
}
Esempio n. 23
0
func TestServerTokenFail(t *testing.T) {
	issuerURL := url.URL{Scheme: "http", Host: "server.example.com"}
	keyFixture := "goodkey"
	ccFixture := oidc.ClientCredentials{
		ID:     testClientID,
		Secret: clientTestSecret,
	}
	signerFixture := &StaticSigner{sig: []byte("beer"), err: nil}

	tests := []struct {
		signer       jose.Signer
		argCC        oidc.ClientCredentials
		argKey       string
		err          error
		scope        []string
		refreshToken string
	}{
		// control test case to make sure fixtures check out
		{
			// NOTE(ericchiang): This test assumes that the database ID of the first
			// refresh token will be "1".
			signer:       signerFixture,
			argCC:        ccFixture,
			argKey:       keyFixture,
			scope:        []string{"openid", "offline_access"},
			refreshToken: fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
		},

		// no 'offline_access' in 'scope', should get empty refresh token
		{
			signer: signerFixture,
			argCC:  ccFixture,
			argKey: keyFixture,
			scope:  []string{"openid"},
		},

		// unrecognized key
		{
			signer: signerFixture,
			argCC:  ccFixture,
			argKey: "foo",
			err:    oauth2.NewError(oauth2.ErrorInvalidGrant),
			scope:  []string{"openid", "offline_access"},
		},

		// unrecognized client
		{
			signer: signerFixture,
			argCC:  oidc.ClientCredentials{ID: "YYY"},
			argKey: keyFixture,
			err:    oauth2.NewError(oauth2.ErrorInvalidClient),
			scope:  []string{"openid", "offline_access"},
		},

		// signing operation fails
		{
			signer: &StaticSigner{sig: nil, err: errors.New("fail")},
			argCC:  ccFixture,
			argKey: keyFixture,
			err:    oauth2.NewError(oauth2.ErrorServerError),
			scope:  []string{"openid", "offline_access"},
		},
	}

	for i, tt := range tests {
		sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
		sm.GenerateCode = func() (string, error) { return keyFixture, nil }

		sessionID, err := sm.NewSession("connector_id", ccFixture.ID, "bogus", url.URL{}, "", false, tt.scope)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		_, err = sm.AttachRemoteIdentity(sessionID, oidc.Identity{})
		if err != nil {
			t.Errorf("case %d: unexpected error: %v", i, err)
			continue
		}
		km := &StaticKeyManager{
			signer: tt.signer,
		}

		clients := []client.Client{
			client.Client{
				Credentials: ccFixture,
				Metadata: oidc.ClientMetadata{
					RedirectURIs: []url.URL{
						validRedirURL,
					},
				},
			},
		}
		dbm := db.NewMemDB()
		clientIDGenerator := func(hostport string) (string, error) {
			return hostport, nil
		}
		secGen := func() ([]byte, error) {
			return []byte("secret"), nil
		}
		clientRepo := db.NewClientRepo(dbm)
		clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
		if err != nil {
			t.Fatalf("Failed to create client identity manager: %v", err)
		}
		_, err = sm.AttachUser(sessionID, "testid-1")
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}

		userRepo, err := makeNewUserRepo()
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

		srv := &Server{
			IssuerURL:        issuerURL,
			KeyManager:       km,
			SessionManager:   sm,
			ClientRepo:       clientRepo,
			ClientManager:    clientManager,
			UserRepo:         userRepo,
			RefreshTokenRepo: refreshTokenRepo,
		}

		_, err = sm.NewSessionKey(sessionID)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		jwt, token, err := srv.CodeToken(tt.argCC, tt.argKey)
		if token != tt.refreshToken {
			fmt.Printf("case %d: expect refresh token %q, got %q\n", i, tt.refreshToken, token)
			t.Fatalf("case %d: expect refresh token %q, got %q", i, tt.refreshToken, token)
			panic("")
		}
		if !reflect.DeepEqual(err, tt.err) {
			t.Errorf("case %d: expect %v, got %v", i, tt.err, err)
		}
		if err == nil && jwt == nil {
			t.Errorf("case %d: got nil JWT", i)
		}
		if err != nil && jwt != nil {
			t.Errorf("case %d: got non-nil JWT %v", i, jwt)
		}
	}
}
Esempio n. 24
0
func TestHandleAuthFuncResponsesSingleRedirectURL(t *testing.T) {
	idpcs := []connector.Connector{
		&fakeConnector{loginURL: "http://fake.example.com"},
	}
	dbm := db.NewMemDB()
	clients := []client.Client{
		client.Client{
			Credentials: oidc.ClientCredentials{
				ID:     "client.example.com",
				Secret: base64.URLEncoding.EncodeToString([]byte("secret")),
			},
			Metadata: oidc.ClientMetadata{
				RedirectURIs: []url.URL{
					url.URL{Scheme: "http", Host: "client.example.com", Path: "/callback"},
				},
			},
		},
	}

	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	srv := &Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		SessionManager: manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB())),
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
	}

	tests := []struct {
		query        url.Values
		wantCode     int
		wantLocation string
	}{
		// no redirect_uri provided, but client only has one, so it's usable
		{
			query: url.Values{
				"response_type": []string{"code"},
				"client_id":     []string{"client.example.com"},
				"connector_id":  []string{"fake"},
				"scope":         []string{"openid"},
			},
			wantCode:     http.StatusFound,
			wantLocation: "http://fake.example.com",
		},

		// provided redirect_uri matches client
		{
			query: url.Values{
				"response_type": []string{"code"},
				"redirect_uri":  []string{"http://client.example.com/callback"},
				"client_id":     []string{"client.example.com"},
				"connector_id":  []string{"fake"},
				"scope":         []string{"openid"},
			},
			wantCode:     http.StatusFound,
			wantLocation: "http://fake.example.com",
		},

		// provided redirect_uri does not match client
		{
			query: url.Values{
				"response_type": []string{"code"},
				"redirect_uri":  []string{"http://unrecognized.example.com/callback"},
				"client_id":     []string{"client.example.com"},
				"connector_id":  []string{"fake"},
				"scope":         []string{"openid"},
			},
			wantCode: http.StatusBadRequest,
		},

		// nonexistant client_id
		{
			query: url.Values{
				"response_type": []string{"code"},
				"redirect_uri":  []string{"http://client.example.com/callback"},
				"client_id":     []string{"YYY"},
				"connector_id":  []string{"fake"},
				"scope":         []string{"openid"},
			},
			wantCode: http.StatusBadRequest,
		},

		// unsupported response type, redirects back to client
		{
			query: url.Values{
				"response_type": []string{"token"},
				"client_id":     []string{"client.example.com"},
				"connector_id":  []string{"fake"},
				"scope":         []string{"openid"},
			},
			wantCode:     http.StatusFound,
			wantLocation: "http://client.example.com/callback?error=unsupported_response_type&state=",
		},

		// no 'openid' in scope
		{
			query: url.Values{
				"response_type": []string{"code"},
				"redirect_uri":  []string{"http://client.example.com/callback"},
				"client_id":     []string{"client.example.com"},
				"connector_id":  []string{"fake"},
			},
			wantCode: http.StatusBadRequest,
		},
		// empty response_type
		{
			query: url.Values{
				"redirect_uri": []string{"http://client.example.com/callback"},
				"client_id":    []string{"client.example.com"},
				"connector_id": []string{"fake"},
				"scope":        []string{"openid"},
			},
			wantCode:     http.StatusFound,
			wantLocation: "http://client.example.com/callback?error=unsupported_response_type&state=",
		},

		// empty client_id
		{
			query: url.Values{
				"response_type": []string{"code"},
				"redirect_uri":  []string{"http://unrecognized.example.com/callback"},
				"connector_id":  []string{"fake"},
				"scope":         []string{"openid"},
			},
			wantCode: http.StatusBadRequest,
		},
	}

	for i, tt := range tests {
		hdlr := handleAuthFunc(srv, idpcs, nil, true)
		w := httptest.NewRecorder()
		u := fmt.Sprintf("http://server.example.com?%s", tt.query.Encode())
		req, err := http.NewRequest("GET", u, nil)
		if err != nil {
			t.Errorf("case %d: unable to form HTTP request: %v", i, err)
			continue
		}

		hdlr.ServeHTTP(w, req)
		if tt.wantCode != w.Code {
			t.Errorf("case %d: HTTP code mismatch: want=%d got=%d", i, tt.wantCode, w.Code)
			continue
		}

		gotLocation := w.Header().Get("Location")
		if tt.wantLocation != gotLocation {
			t.Errorf("case %d: HTTP Location header mismatch: want=%s got=%s", i, tt.wantLocation, gotLocation)
		}
	}
}
Esempio n. 25
0
func TestServerCodeToken(t *testing.T) {
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				validRedirURL,
			},
		},
	}
	clients := []client.Client{ci}
	dbm := db.NewMemDB()
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	km := &StaticKeyManager{
		signer: &StaticSigner{sig: []byte("beer"), err: nil},
	}
	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))

	userRepo, err := makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

	srv := &Server{
		IssuerURL:        url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:       km,
		SessionManager:   sm,
		ClientRepo:       clientRepo,
		ClientManager:    clientManager,
		UserRepo:         userRepo,
		RefreshTokenRepo: refreshTokenRepo,
	}

	tests := []struct {
		scope        []string
		refreshToken string
	}{
		// No 'offline_access' in scope, should get empty refresh token.
		{
			scope:        []string{"openid"},
			refreshToken: "",
		},
		// Have 'offline_access' in scope, should get non-empty refresh token.
		{
			// NOTE(ericchiang): This test assumes that the database ID of the first
			// refresh token will be "1".
			scope:        []string{"openid", "offline_access"},
			refreshToken: fmt.Sprintf("1/%s", base64.URLEncoding.EncodeToString([]byte("refresh-1"))),
		},
	}

	for i, tt := range tests {
		sessionID, err := sm.NewSession("bogus_idpc", ci.Credentials.ID, "bogus", url.URL{}, "", false, tt.scope)
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}
		_, err = sm.AttachRemoteIdentity(sessionID, oidc.Identity{})
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}

		_, err = sm.AttachUser(sessionID, "testid-1")
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}

		key, err := sm.NewSessionKey(sessionID)
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}

		jwt, token, err := srv.CodeToken(ci.Credentials, key)
		if err != nil {
			t.Fatalf("case %d: unexpected error: %v", i, err)
		}
		if jwt == nil {
			t.Fatalf("case %d: expect non-nil jwt", i)
		}
		if token != tt.refreshToken {
			t.Fatalf("case %d: expect refresh token %q, got %q", i, tt.refreshToken, token)
		}
	}
}
Esempio n. 26
0
File: main.go Progetto: Tecsisa/dex
func main() {
	fs := flag.NewFlagSet("dex-overlord", flag.ExitOnError)

	keySecrets := pflag.NewBase64List(32)
	fs.Var(keySecrets, "key-secrets", "A comma-separated list of base64 encoded 32 byte strings used as symmetric keys used to encrypt/decrypt signing key data in DB. The first key is considered the active key and used for encryption, while the others are used to decrypt.")

	useOldFormat := fs.Bool("use-deprecated-secret-format", false, "In prior releases, the database used AES-CBC to encrypt keys. New deployments should use the default AES-GCM encryption.")

	dbURL := fs.String("db-url", "", "DSN-formatted database connection string")

	dbMigrate := fs.Bool("db-migrate", true, "perform database migrations when starting up overlord. This includes the initial DB objects creation.")

	keyPeriod := fs.Duration("key-period", 24*time.Hour, "length of time for-which a given key will be valid")
	gcInterval := fs.Duration("gc-interval", time.Hour, "length of time between garbage collection runs")

	adminListen := fs.String("admin-listen", "http://127.0.0.1:5557", "scheme, host and port for listening for administrative operation requests ")

	adminAPISecret := pflag.NewBase64(server.AdminAPISecretLength)
	fs.Var(adminAPISecret, "admin-api-secret", fmt.Sprintf("A base64-encoded %d byte string which is used to protect the Admin API.", server.AdminAPISecretLength))

	localConnectorID := fs.String("local-connector", "local", "ID of the local connector")
	logDebug := fs.Bool("log-debug", false, "log debug-level information")
	logTimestamps := fs.Bool("log-timestamps", false, "prefix log lines with timestamps")

	printVersion := fs.Bool("version", false, "Print the version and exit")

	if err := fs.Parse(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if err := pflag.SetFlagsFromEnv(fs, "DEX_OVERLORD"); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if *printVersion {
		fmt.Printf("dex version %s\ngo version %s\n", strings.TrimPrefix(version, "v"), strings.TrimPrefix(runtime.Version(), "go"))
		os.Exit(0)
	}

	if *logDebug {
		log.EnableDebug()
	}
	if *logTimestamps {
		log.EnableTimestamps()
	}

	adminURL, err := url.Parse(*adminListen)
	if err != nil {
		log.Fatalf("Unable to use --admin-listen flag: %v", err)
	}

	if len(keySecrets.BytesSlice()) == 0 {
		log.Fatalf("Must specify at least one key secret")
	}

	dbCfg := db.Config{
		DSN:                *dbURL,
		MaxIdleConnections: 1,
		MaxOpenConnections: 1,
	}
	dbc, err := db.NewConnection(dbCfg)
	if err != nil {
		log.Fatalf(err.Error())
	}
	if _, ok := dbc.Dialect.(gorp.PostgresDialect); !ok {
		log.Fatal("only postgres backend supported for multi server configurations")
	}

	if *dbMigrate {
		var sleep time.Duration
		for {
			var err error
			var migrations int
			if migrations, err = db.MigrateToLatest(dbc); err == nil {
				log.Infof("Performed %d db migrations", migrations)
				break
			}
			sleep = ptime.ExpBackoff(sleep, time.Minute)
			log.Errorf("Unable to migrate database, retrying in %v: %v", sleep, err)
			time.Sleep(sleep)
		}
	}
	userRepo := db.NewUserRepo(dbc)
	pwiRepo := db.NewPasswordInfoRepo(dbc)
	connCfgRepo := db.NewConnectorConfigRepo(dbc)
	clientRepo := db.NewClientRepo(dbc)
	userManager := manager.NewUserManager(userRepo,
		pwiRepo, connCfgRepo, db.TransactionFactory(dbc), manager.ManagerOptions{})
	clientManager := clientmanager.NewClientManager(clientRepo, db.TransactionFactory(dbc), clientmanager.ManagerOptions{})
	connectorConfigRepo := db.NewConnectorConfigRepo(dbc)

	adminAPI := admin.NewAdminAPI(userRepo, pwiRepo, clientRepo, connectorConfigRepo, userManager, clientManager, *localConnectorID)
	kRepo, err := db.NewPrivateKeySetRepo(dbc, *useOldFormat, keySecrets.BytesSlice()...)
	if err != nil {
		log.Fatalf(err.Error())
	}

	var sleep time.Duration
	for {
		var done bool
		_, err := kRepo.Get()
		switch err {
		case nil:
			done = true
		case key.ErrorNoKeys:
			done = true
		case db.ErrorCannotDecryptKeys:
			log.Fatalf("Cannot decrypt keys using any of the given key secrets. The key secrets must be changed to include one that can decrypt the existing keys, or the existing keys must be deleted.")
		}

		if done {
			break
		}
		sleep = ptime.ExpBackoff(sleep, time.Minute)
		log.Errorf("Unable to get keys from repository, retrying in %v: %v", sleep, err)
		time.Sleep(sleep)
	}

	krot := key.NewPrivateKeyRotator(kRepo, *keyPeriod)
	s := server.NewAdminServer(adminAPI, krot, adminAPISecret.String())
	h := s.HTTPHandler()
	httpsrv := &http.Server{
		Addr:    adminURL.Host,
		Handler: h,
	}

	gc := db.NewGarbageCollector(dbc, *gcInterval)

	log.Infof("Binding to %s...", httpsrv.Addr)
	go func() {
		log.Fatal(httpsrv.ListenAndServe())
	}()

	gc.Run()
	<-krot.Run()
}
Esempio n. 27
0
func TestHTTPExchangeTokenRefreshToken(t *testing.T) {
	password, err := user.NewPasswordFromPlaintext("woof")
	if err != nil {
		t.Fatalf("unexpectd error: %q", err)
	}

	passwordInfo := user.PasswordInfo{
		UserID:   "elroy77",
		Password: password,
	}

	cfg := &connector.LocalConnectorConfig{
		ID: "local",
	}

	validRedirURL := url.URL{
		Scheme: "http",
		Host:   "client.example.com",
		Path:   "/callback",
	}
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     validRedirURL.Host,
			Secret: base64.URLEncoding.EncodeToString([]byte("secret")),
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				validRedirURL,
			},
		},
	}

	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	dbMap := db.NewMemDB()
	clientRepo := db.NewClientRepo(dbMap)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbMap), []client.Client{ci}, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: " + err.Error())
	}
	passwordInfoRepo, err := db.NewPasswordInfoRepoFromPasswordInfos(db.NewMemDB(), []user.PasswordInfo{passwordInfo})
	if err != nil {
		t.Fatalf("Failed to create password info repo: %v", err)
	}

	issuerURL := url.URL{Scheme: "http", Host: "server.example.com"}
	sm := manager.NewSessionManager(db.NewSessionRepo(dbMap), db.NewSessionKeyRepo(dbMap))

	k, err := key.GeneratePrivateKey()
	if err != nil {
		t.Fatalf("Unable to generate RSA key: %v", err)
	}

	km := key.NewPrivateKeyManager()
	err = km.Set(key.NewPrivateKeySet([]*key.PrivateKey{k}, time.Now().Add(time.Minute)))
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	usr := user.User{
		ID:          "ID-test",
		Email:       "*****@*****.**",
		DisplayName: "displayname",
	}
	userRepo := db.NewUserRepo(db.NewMemDB())
	if err := userRepo.Create(nil, usr); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	refreshTokenRepo := refreshtest.NewTestRefreshTokenRepo()

	srv := &server.Server{
		IssuerURL:        issuerURL,
		KeyManager:       km,
		SessionManager:   sm,
		ClientRepo:       clientRepo,
		ClientManager:    clientManager,
		Templates:        template.New(connector.LoginPageTemplateName),
		Connectors:       []connector.Connector{},
		UserRepo:         userRepo,
		PasswordInfoRepo: passwordInfoRepo,
		RefreshTokenRepo: refreshTokenRepo,
	}

	if err = srv.AddConnector(cfg); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	sClient := &phttp.HandlerClient{Handler: srv.HTTPHandler()}
	pcfg, err := oidc.FetchProviderConfig(sClient, issuerURL.String())
	if err != nil {
		t.Fatalf("Failed to fetch provider config: %v", err)
	}

	ks := key.NewPublicKeySet([]jose.JWK{k.JWK()}, time.Now().Add(1*time.Hour))

	ccfg := oidc.ClientConfig{
		HTTPClient:     sClient,
		ProviderConfig: pcfg,
		Credentials:    ci.Credentials,
		RedirectURL:    validRedirURL.String(),
		KeySet:         *ks,
	}

	cl, err := oidc.NewClient(ccfg)
	if err != nil {
		t.Fatalf("Failed creating oidc.Client: %v", err)
	}

	m := http.NewServeMux()

	var claims jose.Claims
	var refresh string

	m.HandleFunc("/callback", handleCallbackFunc(cl, &claims, &refresh))
	cClient := &phttp.HandlerClient{Handler: m}

	// this will actually happen due to some interaction between the
	// end-user and a remote identity provider
	sessionID, err := sm.NewSession("bogus_idpc", ci.Credentials.ID, "bogus", url.URL{}, "", false, []string{"openid", "offline_access"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}
	if _, err = sm.AttachRemoteIdentity(sessionID, passwordInfo.Identity()); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	if _, err = sm.AttachUser(sessionID, usr.ID); err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	key, err := sm.NewSessionKey(sessionID)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	req, err := http.NewRequest("GET", fmt.Sprintf("http://client.example.com/callback?code=%s", key), nil)
	if err != nil {
		t.Fatalf("Failed creating HTTP request: %v", err)
	}

	resp, err := cClient.Do(req)
	if err != nil {
		t.Fatalf("Failed resolving HTTP requests against /callback: %v", err)
	}

	if err := verifyUserClaims(claims, &ci, &usr, issuerURL); err != nil {
		t.Fatalf("Failed to verify claims: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		t.Fatalf("Received status code %d, want %d", resp.StatusCode, http.StatusOK)
	}

	if refresh == "" {
		t.Fatalf("No refresh token")
	}

	// Use refresh token to get a new ID token.
	token, err := cl.RefreshToken(refresh)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	claims, err = token.Claims()
	if err != nil {
		t.Fatalf("Failed parsing claims from client token: %v", err)
	}

	if err := verifyUserClaims(claims, &ci, &usr, issuerURL); err != nil {
		t.Fatalf("Failed to verify claims: %v", err)
	}
}
Esempio n. 28
0
func TestServerLogin(t *testing.T) {
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				url.URL{
					Scheme: "http",
					Host:   "client.example.com",
					Path:   "/callback",
				},
			},
		},
	}

	dbm := db.NewMemDB()
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), []client.Client{ci}, clientmanager.ManagerOptions{})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}

	km := &StaticKeyManager{
		signer: &StaticSigner{sig: []byte("beer"), err: nil},
	}

	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
	sm.GenerateCode = staticGenerateCodeFunc("fakecode")
	sessionID, err := sm.NewSession("test_connector_id", ci.Credentials.ID, "bogus", ci.Metadata.RedirectURIs[0], "", false, []string{"openid"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	userRepo, err := makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	srv := &Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		SessionManager: sm,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
		UserRepo:       userRepo,
	}

	ident := oidc.Identity{ID: "YYY", Name: "elroy", Email: "*****@*****.**"}
	key, err := sm.NewSessionKey(sessionID)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	redirectURL, err := srv.Login(ident, key)
	if err != nil {
		t.Fatalf("Unexpected err from Server.Login: %v", err)
	}

	wantRedirectURL := "http://client.example.com/callback?code=fakecode&state=bogus"
	if wantRedirectURL != redirectURL {
		t.Fatalf("Unexpected redirectURL: want=%q, got=%q", wantRedirectURL, redirectURL)
	}
}
Esempio n. 29
0
func TestServerLoginDisabledUser(t *testing.T) {
	ci := client.Client{
		Credentials: oidc.ClientCredentials{
			ID:     testClientID,
			Secret: clientTestSecret,
		},
		Metadata: oidc.ClientMetadata{
			RedirectURIs: []url.URL{
				validRedirURL,
			},
		},
	}
	clients := []client.Client{ci}
	dbm := db.NewMemDB()
	clientIDGenerator := func(hostport string) (string, error) {
		return hostport, nil
	}
	secGen := func() ([]byte, error) {
		return []byte("secret"), nil
	}
	clientRepo := db.NewClientRepo(dbm)
	clientManager, err := clientmanager.NewClientManagerFromClients(clientRepo, db.TransactionFactory(dbm), clients, clientmanager.ManagerOptions{ClientIDGenerator: clientIDGenerator, SecretGenerator: secGen})
	if err != nil {
		t.Fatalf("Failed to create client identity manager: %v", err)
	}
	km := &StaticKeyManager{
		signer: &StaticSigner{sig: []byte("beer"), err: nil},
	}

	sm := manager.NewSessionManager(db.NewSessionRepo(db.NewMemDB()), db.NewSessionKeyRepo(db.NewMemDB()))
	sm.GenerateCode = staticGenerateCodeFunc("fakecode")
	sessionID, err := sm.NewSession("test_connector_id", ci.Credentials.ID, "bogus", ci.Metadata.RedirectURIs[0], "", false, []string{"openid"})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	userRepo, err := makeNewUserRepo()
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	err = userRepo.Create(nil, user.User{
		ID:       "disabled-1",
		Email:    "*****@*****.**",
		Disabled: true,
	})
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	err = userRepo.AddRemoteIdentity(nil, "disabled-1", user.RemoteIdentity{
		ConnectorID: "test_connector_id",
		ID:          "disabled-connector-id",
	})

	srv := &Server{
		IssuerURL:      url.URL{Scheme: "http", Host: "server.example.com"},
		KeyManager:     km,
		SessionManager: sm,
		ClientRepo:     clientRepo,
		ClientManager:  clientManager,
		UserRepo:       userRepo,
	}

	ident := oidc.Identity{ID: "disabled-connector-id", Name: "elroy", Email: "*****@*****.**"}
	key, err := sm.NewSessionKey(sessionID)
	if err != nil {
		t.Fatalf("Unexpected error: %v", err)
	}

	_, err = srv.Login(ident, key)
	if err == nil {
		t.Errorf("disabled user was allowed to log in")
	}
}
Esempio n. 30
0
func TestClientToken(t *testing.T) {
	now := time.Now()
	tomorrow := now.Add(24 * time.Hour)
	clientMetadata := oidc.ClientMetadata{
		RedirectURIs: []url.URL{
			{Scheme: "https", Host: "authn.example.com", Path: "/callback"},
		},
	}

	dbm := db.NewMemDB()
	clientRepo := db.NewClientRepo(dbm)
	clientManager := clientmanager.NewClientManager(clientRepo, db.TransactionFactory(dbm), clientmanager.ManagerOptions{})
	cli := client.Client{
		Metadata: clientMetadata,
	}
	creds, err := clientManager.New(cli)
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}
	validClientID := creds.ID

	privKey, err := key.GeneratePrivateKey()
	if err != nil {
		t.Fatalf("Failed to generate private key, error=%v", err)
	}
	signer := privKey.Signer()
	pubKey := *key.NewPublicKey(privKey.JWK())

	validIss := "https://example.com"

	makeToken := func(iss, sub, aud string, iat, exp time.Time) string {
		claims := oidc.NewClaims(iss, sub, aud, iat, exp)
		jwt, err := jose.NewSignedJWT(claims, signer)
		if err != nil {
			t.Fatalf("Failed to generate JWT, error=%v", err)
		}
		return jwt.Encode()
	}

	validJWT := makeToken(validIss, validClientID, validClientID, now, tomorrow)
	invalidJWT := makeToken("", "", "", now, tomorrow)

	tests := []struct {
		keys     []key.PublicKey
		manager  *clientmanager.ClientManager
		header   string
		wantCode int
	}{
		// valid token
		{
			keys:     []key.PublicKey{pubKey},
			manager:  clientManager,
			header:   fmt.Sprintf("BEARER %s", validJWT),
			wantCode: http.StatusOK,
		},
		// invalid token
		{
			keys:     []key.PublicKey{pubKey},
			manager:  clientManager,
			header:   fmt.Sprintf("BEARER %s", invalidJWT),
			wantCode: http.StatusUnauthorized,
		},
		// empty header
		{
			keys:     []key.PublicKey{pubKey},
			manager:  clientManager,
			header:   "",
			wantCode: http.StatusUnauthorized,
		},
		// unparsable token
		{
			keys:     []key.PublicKey{pubKey},
			manager:  clientManager,
			header:   "BEARER xxx",
			wantCode: http.StatusUnauthorized,
		},
		// no verification keys
		{
			keys:     []key.PublicKey{},
			manager:  clientManager,
			header:   fmt.Sprintf("BEARER %s", validJWT),
			wantCode: http.StatusUnauthorized,
		},
		// nil repo
		{
			keys:     []key.PublicKey{pubKey},
			manager:  nil,
			header:   fmt.Sprintf("BEARER %s", validJWT),
			wantCode: http.StatusUnauthorized,
		},
		// empty repo
		{
			keys:     []key.PublicKey{pubKey},
			manager:  clientmanager.NewClientManager(db.NewClientRepo(db.NewMemDB()), db.TransactionFactory(db.NewMemDB()), clientmanager.ManagerOptions{}),
			header:   fmt.Sprintf("BEARER %s", validJWT),
			wantCode: http.StatusUnauthorized,
		},
		// client not in repo
		{
			keys:     []key.PublicKey{pubKey},
			manager:  clientManager,
			header:   fmt.Sprintf("BEARER %s", makeToken(validIss, "DOESNT-EXIST", "DOESNT-EXIST", now, tomorrow)),
			wantCode: http.StatusUnauthorized,
		},
	}

	for i, tt := range tests {
		w := httptest.NewRecorder()
		mw := &clientTokenMiddleware{
			issuerURL: validIss,
			ciManager: tt.manager,
			keysFunc: func() ([]key.PublicKey, error) {
				return tt.keys, nil
			},
			next: staticHandler{},
		}
		req := &http.Request{
			Header: http.Header{
				"Authorization": []string{tt.header},
			},
		}

		mw.ServeHTTP(w, req)
		if tt.wantCode != w.Code {
			t.Errorf("case %d: invalid response code, want=%d, got=%d", i, tt.wantCode, w.Code)
		}
	}
}