ory-am/hydra

View on GitHub
consent/handler_test.go

Summary

Maintainability
C
1 day
Test Coverage
// Copyright © 2022 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package consent_test

import (
    "bytes"
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    "net/http/httptest"
    "testing"
    "time"

    "github.com/stretchr/testify/require"

    hydra "github.com/ory/hydra-client-go/v2"
    "github.com/ory/hydra/v2/client"
    . "github.com/ory/hydra/v2/consent"
    "github.com/ory/hydra/v2/flow"
    "github.com/ory/hydra/v2/internal"
    "github.com/ory/hydra/v2/x"
    "github.com/ory/x/contextx"
    "github.com/ory/x/pointerx"
    "github.com/ory/x/sqlxx"
)

func TestGetLogoutRequest(t *testing.T) {
    for k, tc := range []struct {
        exists  bool
        handled bool
        status  int
    }{
        {false, false, http.StatusNotFound},
        {true, false, http.StatusOK},
        {true, true, http.StatusGone},
    } {
        t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) {
            ctx := context.Background()
            key := fmt.Sprint(k)
            challenge := "challenge" + key
            requestURL := "http://192.0.2.1"

            conf := internal.NewConfigurationWithDefaults()
            reg := internal.NewRegistryMemory(t, conf, &contextx.Default{})

            if tc.exists {
                cl := &client.Client{ID: "client" + key}
                require.NoError(t, reg.ClientManager().CreateClient(ctx, cl))
                require.NoError(t, reg.ConsentManager().CreateLogoutRequest(context.TODO(), &flow.LogoutRequest{
                    Client:     cl,
                    ID:         challenge,
                    WasHandled: tc.handled,
                    RequestURL: requestURL,
                }))
            }

            h := NewHandler(reg, conf)
            r := x.NewRouterAdmin(conf.AdminURL)
            h.SetRoutes(r)
            ts := httptest.NewServer(r)
            defer ts.Close()

            c := &http.Client{}
            resp, err := c.Get(ts.URL + "/admin" + LogoutPath + "?challenge=" + challenge)
            require.NoError(t, err)
            require.EqualValues(t, tc.status, resp.StatusCode)

            if tc.handled {
                var result flow.OAuth2RedirectTo
                require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
                require.Equal(t, requestURL, result.RedirectTo)
            } else if tc.exists {
                var result flow.LogoutRequest
                require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
                require.Equal(t, challenge, result.ID)
                require.Equal(t, requestURL, result.RequestURL)
            }
        })
    }
}

func TestGetLoginRequest(t *testing.T) {
    for k, tc := range []struct {
        exists  bool
        handled bool
        status  int
    }{
        {false, false, http.StatusNotFound},
        {true, false, http.StatusOK},
        {true, true, http.StatusGone},
    } {
        t.Run(fmt.Sprintf("exists=%v/handled=%v", tc.exists, tc.handled), func(t *testing.T) {
            ctx := context.Background()
            key := fmt.Sprint(k)
            challenge := "challenge" + key
            requestURL := "http://192.0.2.1"

            conf := internal.NewConfigurationWithDefaults()
            reg := internal.NewRegistryMemory(t, conf, &contextx.Default{})

            if tc.exists {
                cl := &client.Client{ID: "client" + key}
                require.NoError(t, reg.ClientManager().CreateClient(context.Background(), cl))
                f, err := reg.ConsentManager().CreateLoginRequest(context.Background(), &flow.LoginRequest{
                    Client:      cl,
                    ID:          challenge,
                    RequestURL:  requestURL,
                    RequestedAt: time.Now(),
                })
                require.NoError(t, err)
                challenge, err = f.ToLoginChallenge(ctx, reg)
                require.NoError(t, err)

                if tc.handled {
                    _, err := reg.ConsentManager().HandleLoginRequest(ctx, f, challenge, &flow.HandledLoginRequest{ID: challenge, WasHandled: true})
                    require.NoError(t, err)
                    challenge, err = f.ToLoginChallenge(ctx, reg)
                    require.NoError(t, err)
                }
            }

            h := NewHandler(reg, conf)
            r := x.NewRouterAdmin(conf.AdminURL)
            h.SetRoutes(r)
            ts := httptest.NewServer(r)
            defer ts.Close()

            c := &http.Client{}
            resp, err := c.Get(ts.URL + "/admin" + LoginPath + "?challenge=" + challenge)
            require.NoError(t, err)
            require.EqualValues(t, tc.status, resp.StatusCode)

            if tc.handled {
                var result flow.OAuth2RedirectTo
                require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
                require.Equal(t, requestURL, result.RedirectTo)
            } else if tc.exists {
                var result flow.LoginRequest
                require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
                require.Equal(t, challenge, result.ID)
                require.Equal(t, requestURL, result.RequestURL)
                require.NotNil(t, result.Client)
            }
        })
    }
}

func TestGetConsentRequest(t *testing.T) {
    for k, tc := range []struct {
        exists  bool
        handled bool
        status  int
    }{
        {false, false, http.StatusNotFound},
        {true, false, http.StatusOK},
        {true, true, http.StatusGone},
    } {
        t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) {
            ctx := context.Background()
            key := fmt.Sprint(k)
            challenge := "challenge" + key
            requestURL := "http://192.0.2.1"

            conf := internal.NewConfigurationWithDefaults()
            reg := internal.NewRegistryMemory(t, conf, &contextx.Default{})

            if tc.exists {
                cl := &client.Client{ID: "client" + key}
                require.NoError(t, reg.ClientManager().CreateClient(ctx, cl))
                lr := &flow.LoginRequest{
                    ID:          "login-" + challenge,
                    Client:      cl,
                    RequestURL:  requestURL,
                    RequestedAt: time.Now(),
                }
                f, err := reg.ConsentManager().CreateLoginRequest(ctx, lr)
                require.NoError(t, err)
                challenge, err = f.ToLoginChallenge(ctx, reg)
                require.NoError(t, err)
                _, err = reg.ConsentManager().HandleLoginRequest(ctx, f, challenge, &flow.HandledLoginRequest{
                    ID: challenge,
                })
                require.NoError(t, err)
                challenge, err = f.ToConsentChallenge(ctx, reg)
                require.NoError(t, err)
                require.NoError(t, reg.ConsentManager().CreateConsentRequest(ctx, f, &flow.OAuth2ConsentRequest{
                    Client:         cl,
                    ID:             challenge,
                    Verifier:       challenge,
                    CSRF:           challenge,
                    LoginChallenge: sqlxx.NullString(lr.ID),
                }))

                if tc.handled {
                    _, err := reg.ConsentManager().HandleConsentRequest(ctx, f, &flow.AcceptOAuth2ConsentRequest{
                        ID:         challenge,
                        WasHandled: true,
                        HandledAt:  sqlxx.NullTime(time.Now()),
                    })
                    require.NoError(t, err)
                    challenge, err = f.ToConsentChallenge(ctx, reg)
                    require.NoError(t, err)
                }
            }

            h := NewHandler(reg, conf)

            r := x.NewRouterAdmin(conf.AdminURL)
            h.SetRoutes(r)
            ts := httptest.NewServer(r)
            defer ts.Close()

            c := &http.Client{}
            resp, err := c.Get(ts.URL + "/admin" + ConsentPath + "?challenge=" + challenge)
            require.NoError(t, err)
            require.EqualValues(t, tc.status, resp.StatusCode)

            if tc.handled {
                var result flow.OAuth2RedirectTo
                require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
                require.Equal(t, requestURL, result.RedirectTo)
            } else if tc.exists {
                var result flow.OAuth2ConsentRequest
                require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
                require.Equal(t, challenge, result.ID)
                require.Equal(t, requestURL, result.RequestURL)
                require.NotNil(t, result.Client)
            }
        })
    }
}

func TestGetLoginRequestWithDuplicateAccept(t *testing.T) {
    t.Run("Test get login request with duplicate accept", func(t *testing.T) {
        ctx := context.Background()
        challenge := "challenge"
        requestURL := "http://192.0.2.1"

        conf := internal.NewConfigurationWithDefaults()
        reg := internal.NewRegistryMemory(t, conf, &contextx.Default{})

        cl := &client.Client{ID: "client"}
        require.NoError(t, reg.ClientManager().CreateClient(ctx, cl))
        f, err := reg.ConsentManager().CreateLoginRequest(ctx, &flow.LoginRequest{
            Client:      cl,
            ID:          challenge,
            RequestURL:  requestURL,
            RequestedAt: time.Now(),
        })
        require.NoError(t, err)
        challenge, err = f.ToLoginChallenge(ctx, reg)
        require.NoError(t, err)

        h := NewHandler(reg, conf)
        r := x.NewRouterAdmin(conf.AdminURL)
        h.SetRoutes(r)
        ts := httptest.NewServer(r)
        defer ts.Close()

        c := &http.Client{}

        sub := "sub123"
        acceptLogin := &hydra.AcceptOAuth2LoginRequest{Remember: pointerx.Ptr(true), Subject: sub}

        // marshal User to json
        acceptLoginJson, err := json.Marshal(acceptLogin)
        if err != nil {
            panic(err)
        }

        // set the HTTP method, url, and request body
        req, err := http.NewRequest(http.MethodPut, ts.URL+"/admin"+LoginPath+"/accept?challenge="+challenge, bytes.NewBuffer(acceptLoginJson))
        if err != nil {
            panic(err)
        }

        resp, err := c.Do(req)
        require.NoError(t, err)
        require.EqualValues(t, http.StatusOK, resp.StatusCode)

        var result flow.OAuth2RedirectTo
        require.NoError(t, json.NewDecoder(resp.Body).Decode(&result))
        require.NotNil(t, result.RedirectTo)
        require.Contains(t, result.RedirectTo, "login_verifier")

        req2, err := http.NewRequest(http.MethodPut, ts.URL+"/admin"+LoginPath+"/accept?challenge="+challenge, bytes.NewBuffer(acceptLoginJson))
        if err != nil {
            panic(err)
        }

        resp2, err := c.Do(req2)
        require.NoError(t, err)
        require.EqualValues(t, http.StatusOK, resp2.StatusCode)

        var result2 flow.OAuth2RedirectTo
        require.NoError(t, json.NewDecoder(resp2.Body).Decode(&result2))
        require.NotNil(t, result2.RedirectTo)
        require.Contains(t, result2.RedirectTo, "login_verifier")
    })
}