<commit_before>package sessiontest

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"testing"
	"time"

	"github.com/privacybydesign/gabi/big"
	irma "github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/test"
	"github.com/privacybydesign/irmago/irmaclient"
	"github.com/privacybydesign/irmago/server"

	"github.com/dgrijalva/jwt-go"
	"github.com/stretchr/testify/require"
)

func TestSigningSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	request := getSigningRequest(id)
	sessionHelper(t, request, "signature", nil)
}

func TestDisclosureSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	request := getDisclosureRequest(id)
	responseString := sessionHelper(t, request, "verification", nil)

	// Validate JWT
	claims := struct {
		jwt.StandardClaims
		*server.SessionResult
	}{}
	_, err := jwt.ParseWithClaims(responseString, &claims, func(token *jwt.Token) (interface{}, error) {
		return &JwtServerConfiguration.JwtRSAPrivateKey.PublicKey, nil
	})
	require.NoError(t, err)

	// Check default expiration time
	require.True(t, claims.IssuedAt+irma.DefaultJwtValidity == claims.ExpiresAt)
}

func TestNoAttributeDisclosureSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard")
	request := getDisclosureRequest(id)
	sessionHelper(t, request, "verification", nil)
}

func TestEmptyDisclosure(t *testing.T) {
	// Disclosure request asking for an attribute value that the client doesn't have,
	// and an empty conjunction as first option, which is always chosen by the test session handler
	val := "client doesn't have this attr"
	request := irma.NewDisclosureRequest()
	request.Disclose = irma.AttributeConDisCon{
		irma.AttributeDisCon{
			irma.AttributeCon{},
			irma.AttributeCon{{Type: irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.level"), Value: &val}},
		},
	}

	res := requestorSessionHelper(t, request, nil)
	require.Nil(t, res.Err)
	require.NotNil(t, res.SessionResult)
	require.NotEmpty(t, res.SessionResult.Disclosed) // The outer conjunction was satisfied
	require.Empty(t, res.SessionResult.Disclosed[0]) // by the empty set, so we get no attributes
}

func TestIssuanceSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	request := getCombinedIssuanceRequest(id)
	sessionHelper(t, request, "issue", nil)
}

func TestMultipleIssuanceSession(t *testing.T) {
	request := getMultipleIssuanceRequest()
	sessionHelper(t, request, "issue", nil)
}

func TestDefaultCredentialValidity(t *testing.T) {
	client, handler := parseStorage(t)
	defer test.ClearTestStorage(t, handler.storage)
	request := getIssuanceRequest(true)
	sessionHelper(t, request, "issue", client)
}

func TestIssuanceDisclosureEmptyAttributes(t *testing.T) {
	client, handler := parseStorage(t)
	defer test.ClearTestStorage(t, handler.storage)

	req := getNameIssuanceRequest()
	sessionHelper(t, req, "issue", client)

	// Test disclosing our null attribute
	req2 := getDisclosureRequest(irma.NewAttributeTypeIdentifier("irma-demo.MijnOverheid.fullName.prefix"))
	res := requestorSessionHelper(t, req2, client)
	require.Nil(t, res.Err)
	require.Nil(t, res.Disclosed[0][0].RawValue)
}

func TestIssuanceOptionalZeroLengthAttributes(t *testing.T) {
	req := getNameIssuanceRequest()
	req.Credentials[0].Attributes["prefix"] = ""
	sessionHelper(t, req, "issue", nil)
}

func TestIssuanceOptionalSetAttributes(t *testing.T) {
	req := getNameIssuanceRequest()
	req.Credentials[0].Attributes["prefix"] = "van"
	sessionHelper(t, req, "issue", nil)
}

func TestIssuanceSameAttributesNotSingleton(t *testing.T) {
	client, handler := parseStorage(t)
	defer test.ClearTestStorage(t, handler.storage)

	prevLen := len(client.CredentialInfoList())

	req := getIssuanceRequest(true)
	sessionHelper(t, req, "issue", client)

	req = getIssuanceRequest(false)
	sessionHelper(t, req, "issue", client)
	require.Equal(t, prevLen+1, len(client.CredentialInfoList()))

	// Also check whether this is actually stored
	require.NoError(t, client.Close())
	client, handler = parseExistingStorage(t, handler.storage)
	require.Equal(t, prevLen+1, len(client.CredentialInfoList()))
}

func TestIssuanceBinding(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	request := getCombinedIssuanceRequest(id)

	var bindingCode string
	frontendOptionsHandler := func(handler *TestHandler) {
		bindingCode = setBindingMethod(irma.BindingMethodPin, handler)
	}
	bindingHandler := func(handler *TestHandler) {
		// Below protocol version 2.7 binding is not supported, so then the binding stage is expected to be skipped.
		if extractClientMaxVersion(handler.client).Below(2, 7) {
			return
		}

		require.Equal(t, bindingCode, <-handler.bindingCodeChan)

		// Check whether access to request endpoint is denied as long as binding is not finished
		clientTransport := extractClientTransport(handler.dismisser)
		err := clientTransport.Get("request", struct{}{})
		require.Error(t, err)
		sessionErr := err.(*irma.SessionError)
		require.Equal(t, irma.ErrorApi, sessionErr.ErrorType)
		require.Equal(t, server.ErrorBindingRequired.Status, sessionErr.RemoteError.Status)
		require.Equal(t, string(server.ErrorBindingRequired.Type), sessionErr.RemoteError.ErrorName)

		// Check whether binding cannot be disabled again after client is connected.
		request := irma.NewOptionsRequest()
		result := &irma.SessionOptions{}
		err = handler.frontendTransport.Post("frontend/options", result, request)
		require.Error(t, err)

		err = handler.frontendTransport.Post("frontend/bindingcompleted", nil, nil)
		require.NoError(handler.t, err)
	}
	sessionHelperWithFrontendOptions(t, request, "issue", nil, frontendOptionsHandler, bindingHandler)
}

func TestLargeAttribute(t *testing.T) {
	client, handler := parseStorage(t)
	defer test.ClearTestStorage(t, handler.storage)

	require.NoError(t, client.RemoveStorage())
	client.SetPreferences(irmaclient.Preferences{DeveloperMode: true})

	issuanceRequest := getSpecialIssuanceRequest(false, "1234567890123456789012345678901234567890") // 40 chars
	sessionHelper(t, issuanceRequest, "issue", client)
<commit_msg>Test for specific error when disabling binding after client connected<commit_after>package sessiontest

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"testing"
	"time"

	"github.com/privacybydesign/gabi/big"
	irma "github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/test"
	"github.com/privacybydesign/irmago/irmaclient"
	"github.com/privacybydesign/irmago/server"

	"github.com/dgrijalva/jwt-go"
	"github.com/stretchr/testify/require"
)

func TestSigningSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	request := getSigningRequest(id)
	sessionHelper(t, request, "signature", nil)
}

func TestDisclosureSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	request := getDisclosureRequest(id)
	responseString := sessionHelper(t, request, "verification", nil)

	// Validate JWT
	claims := struct {
		jwt.StandardClaims
		*server.SessionResult
	}{}
	_, err := jwt.ParseWithClaims(responseString, &claims, func(token *jwt.Token) (interface{}, error) {
		return &JwtServerConfiguration.JwtRSAPrivateKey.PublicKey, nil
	})
	require.NoError(t, err)

	// Check default expiration time
	require.True(t, claims.IssuedAt+irma.DefaultJwtValidity == claims.ExpiresAt)
}

func TestNoAttributeDisclosureSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard")
	request := getDisclosureRequest(id)
	sessionHelper(t, request, "verification", nil)
}

func TestEmptyDisclosure(t *testing.T) {
	// Disclosure request asking for an attribute value that the client doesn't have,
	// and an empty conjunction as first option, which is always chosen by the test session handler
	val := "client doesn't have this attr"
	request := irma.NewDisclosureRequest()
	request.Disclose = irma.AttributeConDisCon{
		irma.AttributeDisCon{
			irma.AttributeCon{},
			irma.AttributeCon{{Type: irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.level"), Value: &val}},
		},
	}

	res := requestorSessionHelper(t, request, nil)
	require.Nil(t, res.Err)
	require.NotNil(t, res.SessionResult)
	require.NotEmpty(t, res.SessionResult.Disclosed) // The outer conjunction was satisfied
	require.Empty(t, res.SessionResult.Disclosed[0]) // by the empty set, so we get no attributes
}

func TestIssuanceSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	request := getCombinedIssuanceRequest(id)
	sessionHelper(t, request, "issue", nil)
}

func TestMultipleIssuanceSession(t *testing.T) {
	request := getMultipleIssuanceRequest()
	sessionHelper(t, request, "issue", nil)
}

func TestDefaultCredentialValidity(t *testing.T) {
	client, handler := parseStorage(t)
	defer test.ClearTestStorage(t, handler.storage)
	request := getIssuanceRequest(true)
	sessionHelper(t, request, "issue", client)
}

func TestIssuanceDisclosureEmptyAttributes(t *testing.T) {
	client, handler := parseStorage(t)
	defer test.ClearTestStorage(t, handler.storage)

	req := getNameIssuanceRequest()
	sessionHelper(t, req, "issue", client)

	// Test disclosing our null attribute
	req2 := getDisclosureRequest(irma.NewAttributeTypeIdentifier("irma-demo.MijnOverheid.fullName.prefix"))
	res := requestorSessionHelper(t, req2, client)
	require.Nil(t, res.Err)
	require.Nil(t, res.Disclosed[0][0].RawValue)
}

func TestIssuanceOptionalZeroLengthAttributes(t *testing.T) {
	req := getNameIssuanceRequest()
	req.Credentials[0].Attributes["prefix"] = ""
	sessionHelper(t, req, "issue", nil)
}

func TestIssuanceOptionalSetAttributes(t *testing.T) {
	req := getNameIssuanceRequest()
	req.Credentials[0].Attributes["prefix"] = "van"
	sessionHelper(t, req, "issue", nil)
}

func TestIssuanceSameAttributesNotSingleton(t *testing.T) {
	client, handler := parseStorage(t)
	defer test.ClearTestStorage(t, handler.storage)

	prevLen := len(client.CredentialInfoList())

	req := getIssuanceRequest(true)
	sessionHelper(t, req, "issue", client)

	req = getIssuanceRequest(false)
	sessionHelper(t, req, "issue", client)
	require.Equal(t, prevLen+1, len(client.CredentialInfoList()))

	// Also check whether this is actually stored
	require.NoError(t, client.Close())
	client, handler = parseExistingStorage(t, handler.storage)
	require.Equal(t, prevLen+1, len(client.CredentialInfoList()))
}

func TestIssuanceBinding(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	request := getCombinedIssuanceRequest(id)

	var bindingCode string
	frontendOptionsHandler := func(handler *TestHandler) {
		bindingCode = setBindingMethod(irma.BindingMethodPin, handler)
	}
	bindingHandler := func(handler *TestHandler) {
		// Below protocol version 2.7 binding is not supported, so then the binding stage is expected to be skipped.
		if extractClientMaxVersion(handler.client).Below(2, 7) {
			return
		}

		require.Equal(t, bindingCode, <-handler.bindingCodeChan)

		// Check whether access to request endpoint is denied as long as binding is not finished
		clientTransport := extractClientTransport(handler.dismisser)
		err := clientTransport.Get("request", struct{}{})
		require.Error(t, err)
		sessionErr := err.(*irma.SessionError)
		require.Equal(t, irma.ErrorApi, sessionErr.ErrorType)
		require.Equal(t, server.ErrorBindingRequired.Status, sessionErr.RemoteError.Status)
		require.Equal(t, string(server.ErrorBindingRequired.Type), sessionErr.RemoteError.ErrorName)

		// Check whether binding cannot be disabled again after client is connected.
		request := irma.NewOptionsRequest()
		result := &irma.SessionOptions{}
		err = handler.frontendTransport.Post("frontend/options", result, request)
		require.Error(t, err)
		sessionErr = err.(*irma.SessionError)
		require.Equal(t, irma.ErrorApi, sessionErr.ErrorType)
		require.Equal(t, server.ErrorUnexpectedRequest.Status, sessionErr.RemoteError.Status)
		require.Equal(t, string(server.ErrorUnexpectedRequest.Type), sessionErr.RemoteError.ErrorName)

		err = handler.frontendTransport.Post("frontend/bindingcompleted", nil, nil)
		require.NoError(handler.t, err)
	}
	sessionHelperWithFrontendOptions(t, request, "issue", nil, frontendOptionsHandler, bindingHandler)
}

func TestLargeAttribute(t *testing.T) {
	client, handler := parseStorage(t)
	defer test.ClearTestStorage(t, handler.storage)

	require.NoError(t, client.RemoveStorage())
	client.SetPreferences(irmaclient.Preferences{DeveloperMode: true})

	issuanceRequest := getSpecialIssuanceRequest(false, "1234567890123456789012345678901234567890") // 40 chars
	sessionHelper(t, issuanceRequest, "issue", client)

{'fraction_non_alphanumeric': 0.12387126649687427, 'fraction_numerical': 0.008489619510689203, 'mean_word_length': 6.053892215568863, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4652156', 'n_tokens_mistral': 4370, 'n_tokens_neox': 4120, 'n_words': 850}