Newer
Older
import (
"context"
"testing"
"github.com/coreos/dex/api"
"github.com/coreos/dex/server/internal"
"github.com/coreos/dex/storage"
"github.com/coreos/dex/storage/memory"
"google.golang.org/grpc"
)
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// apiClient is a test gRPC client. When constructed, it runs a server in
// the background to exercise the serialization and network configuration
// instead of just this package's server implementation.
type apiClient struct {
// Embedded gRPC client to talk to the server.
api.DexClient
// Close releases resources associated with this client, includuing shutting
// down the background server.
Close func()
}
// newAPI constructs a gRCP client connected to a backing server.
func newAPI(s storage.Storage, logger logrus.FieldLogger, t *testing.T) *apiClient {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
serv := grpc.NewServer()
api.RegisterDexServer(serv, NewAPI(s, logger))
go serv.Serve(l)
// Dial will retry automatically if the serv.Serve() goroutine
// hasn't started yet.
conn, err := grpc.Dial(l.Addr().String(), grpc.WithInsecure())
if err != nil {
t.Fatal(err)
}
return &apiClient{
DexClient: api.NewDexClient(conn),
Close: func() {
conn.Close()
serv.Stop()
l.Close()
},
}
}
// Attempts to create, update and delete a test Password
func TestPassword(t *testing.T) {
logger := &logrus.Logger{
Out: os.Stderr,
Formatter: &logrus.TextFormatter{DisableColors: true},
Level: logrus.DebugLevel,
}
s := memory.New(logger)
client := newAPI(s, logger, t)
defer client.Close()
ctx := context.Background()
p := api.Password{
Email: "test@example.com",
// bcrypt hash of the value "test1" with cost 10
Hash: []byte("$2a$10$XVMN/Fid.Ks4CXgzo8fpR.iU1khOMsP5g9xQeXuBm1wXjRX8pjUtO"),
Username: "test",
UserId: "test123",
}
createReq := api.CreatePasswordReq{
Password: &p,
}
if resp, err := client.CreatePassword(ctx, &createReq); err != nil || resp.AlreadyExists {
if resp.AlreadyExists {
t.Fatalf("Unable to create password since %s already exists", createReq.Password.Email)
}
t.Fatalf("Unable to create password: %v", err)
}
// Attempt to create a password that already exists.
if resp, _ := client.CreatePassword(ctx, &createReq); !resp.AlreadyExists {
t.Fatalf("Created password %s twice", createReq.Password.Email)
}
updateReq := api.UpdatePasswordReq{
Email: "test@example.com",
NewUsername: "test1",
}
if _, err := client.UpdatePassword(ctx, &updateReq); err != nil {
t.Fatalf("Unable to update password: %v", err)
}
pass, err := s.GetPassword(updateReq.Email)
if err != nil {
t.Fatalf("Unable to retrieve password: %v", err)
}
if pass.Username != updateReq.NewUsername {
t.Fatalf("UpdatePassword failed. Expected username %s retrieved %s", updateReq.NewUsername, pass.Username)
}
deleteReq := api.DeletePasswordReq{
Email: "test@example.com",
}
if _, err := client.DeletePassword(ctx, &deleteReq); err != nil {
t.Fatalf("Unable to delete password: %v", err)
}
}
// Ensures checkCost returns expected values
func TestCheckCost(t *testing.T) {
logger := &logrus.Logger{
Out: os.Stderr,
Formatter: &logrus.TextFormatter{DisableColors: true},
Level: logrus.DebugLevel,
}
s := memory.New(logger)
client := newAPI(s, logger, t)
defer client.Close()
tests := []struct {
name string
inputHash []byte
wantErr bool
}{
{
name: "valid cost",
// bcrypt hash of the value "test1" with cost 12 (default)
inputHash: []byte("$2a$12$M2Ot95Qty1MuQdubh1acWOiYadJDzeVg3ve4n5b.dgcgPdjCseKx2"),
},
{
name: "invalid hash",
inputHash: []byte(""),
wantErr: true,
},
{
name: "cost below default",
// bcrypt hash of the value "test1" with cost 4
inputHash: []byte("$2a$04$8bSTbuVCLpKzaqB3BmgI7edDigG5tIQKkjYUu/mEO9gQgIkw9m7eG"),
wantErr: true,
},
{
name: "cost above recommendation",
// bcrypt hash of the value "test1" with cost 17
inputHash: []byte("$2a$17$tWuZkTxtSmRyWZAGWVHQE.7npdl.TgP8adjzLJD.SyjpFznKBftPe"),
wantErr: true,
},
}
for _, tc := range tests {
if err := checkCost(tc.inputHash); err != nil {
if !tc.wantErr {
t.Errorf("%s: %s", tc.name, err)
}
continue
}
if tc.wantErr {
t.Errorf("%s: expected err", tc.name)
continue
}
}
}
// Attempts to list and revoke an exisiting refresh token.
func TestRefreshToken(t *testing.T) {
logger := &logrus.Logger{
Out: os.Stderr,
Formatter: &logrus.TextFormatter{DisableColors: true},
Level: logrus.DebugLevel,
}
s := memory.New(logger)
client := newAPI(s, logger, t)
defer client.Close()
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
ctx := context.Background()
// Creating a storage with an existing refresh token and offline session for the user.
id := storage.NewID()
r := storage.RefreshToken{
ID: id,
Token: "bar",
Nonce: "foo",
ClientID: "client_id",
ConnectorID: "client_secret",
Scopes: []string{"openid", "email", "profile"},
CreatedAt: time.Now().UTC().Round(time.Millisecond),
LastUsed: time.Now().UTC().Round(time.Millisecond),
Claims: storage.Claims{
UserID: "1",
Username: "jane",
Email: "jane.doe@example.com",
EmailVerified: true,
Groups: []string{"a", "b"},
},
ConnectorData: []byte(`{"some":"data"}`),
}
if err := s.CreateRefresh(r); err != nil {
t.Fatalf("create refresh token: %v", err)
}
tokenRef := storage.RefreshTokenRef{
ID: r.ID,
ClientID: r.ClientID,
CreatedAt: r.CreatedAt,
LastUsed: r.LastUsed,
}
session := storage.OfflineSessions{
UserID: r.Claims.UserID,
ConnID: r.ConnectorID,
Refresh: make(map[string]*storage.RefreshTokenRef),
}
session.Refresh[tokenRef.ClientID] = &tokenRef
if err := s.CreateOfflineSessions(session); err != nil {
t.Fatalf("create offline session: %v", err)
}
subjectString, err := internal.Marshal(&internal.IDTokenSubject{
UserId: r.Claims.UserID,
ConnId: r.ConnectorID,
})
if err != nil {
t.Errorf("failed to marshal offline session ID: %v", err)
}
//Testing the api.
listReq := api.ListRefreshReq{
UserId: subjectString,
}
listResp, err := client.ListRefresh(ctx, &listReq)
t.Fatalf("Unable to list refresh tokens for user: %v", err)
}
for _, tok := range listResp.RefreshTokens {
if tok.CreatedAt != r.CreatedAt.Unix() {
t.Errorf("Expected CreatedAt timestamp %v, got %v", r.CreatedAt.Unix(), tok.CreatedAt)
}
if tok.LastUsed != r.LastUsed.Unix() {
t.Errorf("Expected LastUsed timestamp %v, got %v", r.LastUsed.Unix(), tok.LastUsed)
}
}
revokeReq := api.RevokeRefreshReq{
UserId: subjectString,
ClientId: r.ClientID,
}
resp, err := client.RevokeRefresh(ctx, &revokeReq)
if err != nil {
t.Fatalf("Unable to revoke refresh tokens for user: %v", err)
}
if resp.NotFound {
t.Errorf("refresh token session wasn't found")
}
// Try to delete again.
//
// See https://github.com/coreos/dex/issues/1055
resp, err = client.RevokeRefresh(ctx, &revokeReq)
if err != nil {
t.Fatalf("Unable to revoke refresh tokens for user: %v", err)
}
if !resp.NotFound {
t.Errorf("refresh token session was found")
}
if resp, _ := client.ListRefresh(ctx, &listReq); len(resp.RefreshTokens) != 0 {
t.Fatalf("Refresh token returned inspite of revoking it.")
}
}