package service import ( "context" fmt "fmt" "net/url" "strings" "testing" "github.com/keys-pub/keys" "github.com/keys-pub/keys/http" "github.com/keys-pub/keys/user" "github.com/stretchr/testify/require" ) func TestUserSearch(t *testing.T) { env := newTestEnv(t) service, closeFn := newTestService(t, env) defer closeFn() ctx := context.TODO() testAuthSetup(t, service) testImportKey(t, service, alice) testUserSetupGithub(t, env, service, alice, "alice") testPush(t, service, alice) testImportKey(t, service, bob) testUserSetupGithub(t, env, service, bob, "bob") testPush(t, service, bob) // Search all resp, err := service.UserSearch(ctx, &UserSearchRequest{}) require.NoError(t, err) require.Equal(t, 2, len(resp.Users)) require.Equal(t, alice.ID().String(), resp.Users[0].KID) require.Equal(t, "alice", resp.Users[0].Name) require.Equal(t, bob.ID().String(), resp.Users[1].KID) // Search "alice" resp, err = service.UserSearch(ctx, &UserSearchRequest{ Query: "alice", }) require.NoError(t, err) require.Equal(t, 1, len(resp.Users)) require.Equal(t, alice.ID().String(), resp.Users[0].KID) // Search "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077" resp, err = service.UserSearch(ctx, &UserSearchRequest{ Query: "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077", }) require.NoError(t, err) require.Equal(t, 1, len(resp.Users)) require.Equal(t, alice.ID().String(), resp.Users[0].KID) // TODO: Test stale result } func TestUser(t *testing.T) { env := newTestEnv(t) service, closeFn := newTestService(t, env) defer closeFn() ctx := context.TODO() testAuthSetup(t, service) testImportKey(t, service, alice) testUserSetupGithub(t, env, service, alice, "alice") resp, err := service.User(ctx, &UserRequest{ KID: alice.ID().String(), }) require.NoError(t, err) require.NotNil(t, resp.User) require.Equal(t, alice.ID().String(), resp.User.KID) key := keys.GenerateEdX25519Key() resp, err = service.User(ctx, &UserRequest{ KID: key.ID().String(), }) require.NoError(t, err) require.Nil(t, resp.User) } func TestUserService(t *testing.T) { env := newTestEnv(t) service, closeFn := newTestService(t, env) defer closeFn() ctx := context.TODO() testAuthSetup(t, service) testImportKey(t, service, alice) _, err := service.UserService(ctx, &UserServiceRequest{ KID: alice.ID().String(), Service: "github", }) require.NoError(t, err) _, err = service.UserService(ctx, &UserServiceRequest{ KID: alice.ID().String(), Service: "twitter", }) require.NoError(t, err) } func TestUserSign(t *testing.T) { env := newTestEnv(t) service, closeFn := newTestService(t, env) defer closeFn() ctx := context.TODO() testAuthSetup(t, service) testImportKey(t, service, alice) resp, err := service.UserSign(ctx, &UserSignRequest{ KID: alice.ID().String(), Service: "github", Name: "alice", }) require.NoError(t, err) require.Equal(t, resp.Name, "alice") usr := &user.User{ KID: alice.ID(), Service: "github", Name: "alice", } err = usr.Verify(resp.Message) require.NoError(t, err) require.Equal(t, "alice", usr.Name) require.Equal(t, "github", usr.Service) require.Equal(t, alice.ID().String(), usr.KID.String()) } func TestUserAdd(t *testing.T) { // SetLogger(NewLogger(DebugLevel)) // keys.SetLogger(NewLogger(DebugLevel)) env := newTestEnv(t) service, closeFn := newTestService(t, env) defer closeFn() ctx := context.TODO() testAuthSetup(t, service) testImportKey(t, service, alice) testUserSetupGithub(t, env, service, alice, "alice") testPush(t, service, alice) sc, err := service.scs.Sigchain(alice.ID()) require.NoError(t, err) require.Equal(t, 1, len(sc.Statements())) resp, err := service.UserSearch(context.TODO(), &UserSearchRequest{}) require.NoError(t, err) require.Equal(t, 1, len(resp.Users)) require.Equal(t, "alice", resp.Users[0].Name) _, err = userSetupGithub(env, service, alice, "alice2") require.EqualError(t, err, "failed to generate user statement: user set in sigchain already") sc2, err := service.scs.Sigchain(alice.ID()) require.NoError(t, err) require.Equal(t, 1, len(sc2.Statements())) resp, err = service.UserSearch(context.TODO(), &UserSearchRequest{}) require.NoError(t, err) require.Equal(t, 1, len(resp.Users)) require.Equal(t, "alice", resp.Users[0].Name) // Try to add user for a public key (not owned) randSPK := keys.GenerateEdX25519Key() randID := randSPK.ID() _, err = service.UserAdd(ctx, &UserAddRequest{ KID: randID.String(), Service: "github", Name: "bob", URL: "https://gist.github.com/bob/1", }) require.EqualError(t, err, fmt.Sprintf("%s not found", randID)) // Invalid scheme _, err = service.UserAdd(ctx, &UserAddRequest{ KID: alice.String(), Service: "github", Name: "bob", URL: "file://gist.github.com/alice/1", }) require.EqualError(t, err, "invalid scheme for url file://gist.github.com/alice/1") } func TestUserAddGithub(t *testing.T) { env := newTestEnv(t) service, closeFn := newTestService(t, env) defer closeFn() testAuthSetup(t, service) testImportKey(t, service, bob) resp, err := service.UserSign(context.TODO(), &UserSignRequest{ KID: bob.ID().String(), Service: "github", Name: "bob", }) require.NoError(t, err) env.client.SetProxy("", func(ctx context.Context, req *http.Request, headers []http.Header) http.ProxyResponse { return http.ProxyResponse{Body: []byte(githubMock("bob", "1", resp.Message))} }) // Bob addResp, err := service.UserAdd(context.TODO(), &UserAddRequest{ KID: bob.ID().String(), Service: "github", Name: "Bob", URL: "https://gist.github.com/Bob/1", }) require.NoError(t, err) require.NotEmpty(t, addResp) require.NotEmpty(t, addResp.User) require.Equal(t, "bob", addResp.User.Name) require.Equal(t, "https://gist.github.com/bob/1", addResp.User.URL) } func TestUserAddReddit(t *testing.T) { env := newTestEnv(t) service, closeFn := newTestService(t, env) defer closeFn() testAuthSetup(t, service) testImportKey(t, service, bob) resp, err := service.UserSign(context.TODO(), &UserSignRequest{ KID: bob.ID().String(), Service: "reddit", Name: "bob", }) require.NoError(t, err) // url := "https://www.reddit.com/r/keyspubmsgs/comments/123/bob" rmsg := redditMock("bob", resp.Message, "keyspubmsgs") env.client.SetProxy("", func(ctx context.Context, req *http.Request, headers []http.Header) http.ProxyResponse { return http.ProxyResponse{Body: []byte(rmsg)} }) // Bob, with funky URL input _, err = service.UserAdd(context.TODO(), &UserAddRequest{ KID: bob.ID().String(), Service: "reddit", Name: "bob", URL: "https://old.reddit.com/r/keyspubmsgs/comments/123/bob/?testing=1", }) require.NoError(t, err) // "Bob" sign _, err = service.UserSign(context.TODO(), &UserSignRequest{ KID: bob.ID().String(), Service: "reddit", Name: "Bob", }) require.NoError(t, err) // Revoke _, err = service.StatementRevoke(context.TODO(), &StatementRevokeRequest{ Seq: 1, KID: bob.ID().String(), }) require.NoError(t, err) // "Bob" add _, err = service.UserAdd(context.TODO(), &UserAddRequest{ KID: bob.ID().String(), Service: "reddit", Name: "Bob", URL: "https://old.reddit.com/r/keyspubmsgs/comments/123/bob", }) require.NoError(t, err) } func TestUserAddEcho(t *testing.T) { env := newTestEnv(t) service, closeFn := newTestService(t, env) defer closeFn() testAuthSetup(t, service) testImportKey(t, service, bob) signResp, err := service.UserSign(context.TODO(), &UserSignRequest{ KID: bob.ID().String(), Service: "echo", Name: "bob", }) require.NoError(t, err) msg := url.QueryEscape(strings.ReplaceAll(signResp.Message, "\n", " ")) require.Equal(t, `BEGIN+MESSAGE.+TekiZiJ7UHFcsrE+wEBuLuJgb4FfKzv+dV9Lb2hdVh2owd7+vQv1O19ku8c3wIw+KvH7czoVeRdgZhJ+6J1T6sCluySTCKq+6Xr2MZHgg70jqKK+mDTzoUxf2jtfxAz+fAY1eUMoAR5Dkza+6QbeBrUgPDLCzsB+ypoKGupJeZt3t5K+f9I6diI2kqKmjo1+UKFzfQqTuOyoAsS+BrjFuDwe52ZvqLl+.+END+MESSAGE.`, msg) urs := "test://echo/bob/" + bob.ID().String() + "/" + msg addResp, err := service.UserAdd(context.TODO(), &UserAddRequest{ KID: bob.ID().String(), Service: "echo", Name: "bob", URL: urs, }) require.NoError(t, err) require.Equal(t, "bob@echo", addResp.User.ID) // user@echo should be hidden from search searchResp, err := service.UserSearch(context.TODO(), &UserSearchRequest{}) require.NoError(t, err) require.Equal(t, 0, len(searchResp.Users)) kid, err := service.lookup(context.TODO(), "bob@echo", nil) require.NoError(t, err) require.Equal(t, keys.ID("kex1syuhwr4g05t4744r23nvxnr7en9cmz53knhr0gja7c84hr7fkw2quf6zcg"), kid) } func TestSearchUsers(t *testing.T) { env := newTestEnv(t) service, closeFn := newTestService(t, env) defer closeFn() ctx := context.TODO() testAuthSetup(t, service) testImportKey(t, service, alice) for i := 0; i < 3; i++ { keyResp, err := service.KeyGenerate(ctx, &KeyGenerateRequest{Type: string(keys.EdX25519)}) require.NoError(t, err) username := fmt.Sprintf("username%d", i) key, err := service.vault.Key(keys.ID(keyResp.KID)) require.NoError(t, err) testUserSetupGithub(t, env, service, key.AsEdX25519(), username) } resp, err := service.UserSearch(ctx, &UserSearchRequest{}) require.NoError(t, err) require.Equal(t, 3, len(resp.Users)) require.Equal(t, "username0", resp.Users[0].Name) require.Equal(t, "username1", resp.Users[1].Name) require.Equal(t, "username2", resp.Users[2].Name) }