diff --git a/.gitignore b/.gitignore index 222e55661..3962675d1 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ iam-proxy-italia-project/logs/*.log iam-proxy-italia-project/metadata/*.md iam-proxy-italia-project/data/* iam-proxy-italia-project/private/* +iam-proxy-italia-project/wwwallet/mysql/data/ +iam-proxy-italia-project/wwwallet/mysql/init/ *.pyc *pyFF_example/info.log *pyFF_example/error.log @@ -26,8 +28,11 @@ Docker-compose/iam-proxy-italia-project/* Docker-compose/djangosaml2_sp/* Docker-compose/mongo/db/* Docker-compose/nginx/html/static/* +Docker-compose/nginx/conf.d/sites-enabled/wwwallet.conf +Docker-compose/nginx/conf.d/wwwallet.default.conf Docker-compose/certbot/live/localhost Docker-compose/.env +Docker-compose/wwwallet/* Docker-compose/eudi-wallet-it-python # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..c6463b0dc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "iam-proxy-italia-project/wwwallet/wallet-frontend"] + path = iam-proxy-italia-project/wwwallet/wallet-frontend + url = https://github.com/wwWallet/wallet-frontend.git +[submodule "iam-proxy-italia-project/wwwallet/wallet-backend-server"] + path = iam-proxy-italia-project/wwwallet/wallet-backend-server + url = https://github.com/wwWallet/wallet-backend-server +[submodule "iam-proxy-italia-project/wwwallet/wallet-common"] + path = iam-proxy-italia-project/wwwallet/wallet-common + url = https://github.com/wwWallet/wallet-common diff --git a/Docker-compose/README.md b/Docker-compose/README.md index 88280f53a..bf8a14512 100644 --- a/Docker-compose/README.md +++ b/Docker-compose/README.md @@ -32,8 +32,10 @@ cd Docker-compose The script creates the directories for local mounts and copies all required files to start a full demo with test and SAML2 Service Providers. The script can be run with different options: --`f` cleans the folders; if combined with `-e` (`-e -f`), it also overrides the .env file. --`t` to run tests with `spid_sp_test` in a pipeline-like execution; + +-`f` cleans the folders; if combined with `-e` (`-e -f`), it also overrides the .env file. +-`t` to run tests with `spid_sp_test` in a pipeline-like execution; +-`w` to run the wwwallet profile, view [wwwallet_setup](../docs/readme.wwwallet_setup.md) for more details. > ⚠️ Warning: The script deletes any previous created directory if found. diff --git a/Docker-compose/docker-compose.yml b/Docker-compose/docker-compose.yml index a4a0b008c..447cfd52b 100644 --- a/Docker-compose/docker-compose.yml +++ b/Docker-compose/docker-compose.yml @@ -1,10 +1,75 @@ services: + wwwallet-mariadb: + profiles: + - wwwallet + container_name: wwwallet-mariadb + environment: + MARIADB_DATABASE: ${MARIADB_DBNAME:-wwwalletdb} + MARIADB_ROOT_PASSWORD: ${MARIADB_DBPASSWORD:-changeme} + MARIADB_USER: ${MARIADB_DBUSER:-dbuser} + MARIADB_PASSWORD: ${MARIADB_DBPASSWORD:-dbpassword} + command: + - --table_definition_cache=100 + - --performance_schema=0 + - --innodb_use_native_aio=0 + volumes: + - ./wwwallet/mariadb/data/:/var/lib/mysql + image: mariadb:10.6 + ports: + - "3306:3306" + networks: + - iam-proxy-italia + + wwwallet-server: + profiles: + - wwwallet + image: wwwallet-server:latest + build: + context: ./wwwallet/wallet-backend-server + dockerfile: Dockerfile + container_name: wwwallet-server + depends_on: + - wwwallet-mariadb + ports: + - "5000:5000" + networks: + - iam-proxy-italia + volumes: + - ./nginx/conf.d/wwwallet.default.conf:/etc/nginx/conf.d/default.conf:ro + - ./certbot/live/${SATOSA_HOSTNAME:-localhost}:/etc/nginx/certs:ro + environment: + - NGINX_HOST=${SATOSA_HOSTNAME:-localhost} + - TZ=${TZ:-Europe/Rome} + - NODE_TLS_REJECT_UNAUTHORIZED=0 + command: > + bash -c " + apt-get update && apt-get install -y nginx && \ + nginx -g 'daemon off;' & + node ./dist/src/app.js + " + + wwwallet-frontend: + profiles: + - wwwallet + image: wwwallet-frontend:latest + build: + context: ./wwwallet/wallet-frontend + dockerfile: Dockerfile + container_name: wwwallet-frontend + depends_on: + - wwwallet-server + ports: + - "3000:3000" + networks: + - iam-proxy-italia + satosa-mongo: profiles: - demo - mongo - mongoexpress + - wwwallet image: mongo container_name: satosa-mongo environment: @@ -25,6 +90,7 @@ services: profiles: - demo - mongoexpress + - wwwallet image: mongo-express container_name: satosa-mongo-express ports: @@ -45,6 +111,7 @@ services: profiles: - demo - dev + - wwwallet build: context: ../ args: @@ -54,7 +121,7 @@ services: working_dir: /django_sp entrypoint: "sh ../entrypoint.sh" volumes: - - ./djangosaml2_sp:/django_sp:rw + - ./djangosaml2_sp:/django_sp:rw ports: - "8000:8000" networks: @@ -63,11 +130,11 @@ services: iam-proxy-italia: # image: ghcr.io/italia/iam-proxy-italia:latest image: iam-proxy-italia:3.0 - build: - context: ../ - args: - - NODE_ENV=local - dockerfile: Dockerfile + build: + context: ../ + args: + - NODE_ENV=local + dockerfile: Dockerfile container_name: iam-proxy-italia # depends_on: # - satosa-mongo @@ -87,9 +154,10 @@ services: - SATOSA_PUBLIC_KEY=${SATOSA_KEYS_FOLDER:-./pki}/${SATOSA_PUBLIC_KEY_FILENAME:-cert.pem} - SATOSA_BASE=https://${SATOSA_HOSTNAME:-localhost} - SATOSA_BASE_STATIC=${SATOSA_BASE}/static + - SATOSA_BASE_OPENID4VCI=${SATOSA_BASE}/OpenID4VCI - SATOSA_DISCO_SRV=${SATOSA_BASE_STATIC}/disco.html - SATOSA_UNKNOW_ERROR_REDIRECT_PAGE=${SATOSA_BASE_STATIC}/error_page.html - + - MONGODB_USERNAME=${MONGO_DBUSER:-satosa} - MONGODB_PASSWORD=${MONGO_DBPASSWORD:-thatpassword} - SATOSA_ENCRYPTION_KEY=${SATOSA_ENCRYPTION_KEY:-CHANGE_ME!} @@ -141,6 +209,12 @@ services: timeout: 10s satosa-nginx: + profiles: + - demo + - mongo + - mongoexpress + - dev + - wwwallet image: nginx:alpine container_name: satosa-nginx depends_on: @@ -151,7 +225,9 @@ services: ports: - "443:443" volumes: - - ./nginx/conf.d:/etc/nginx/conf.d:ro + - ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf:ro + - ./nginx/conf.d/sites-enabled:/etc/nginx/conf.d/sites-enabled:ro + - ./nginx/html:/etc/nginx/html:ro - ./nginx/html:/usr/share/nginx/html:ro - ./certbot/archive:/etc/archive:ro - ./certbot/live/${SATOSA_HOSTNAME:-localhost}:/etc/nginx/certs:ro @@ -166,7 +242,8 @@ services: profiles: - demo - dev - image: italia/spid-saml-check + - wwwallet + image: italia/spid-saml-check:1.10.6 container_name: spid-samlcheck ports: - "8443:8443" @@ -174,7 +251,7 @@ services: - iam-proxy-italia satosa-certbot: image: certbot/certbot - container_name: satosa-certbot + container_name: satosa-certbot environment: - CERTBOT_ENABLED=${SATOSA_CERTBOT_ENABLED:-true} - CERTBOT_EMAIL=${SATOSA_CONTACT_PERSON_EMAIL_ADDRESS:-support.example@organization.org} diff --git a/Docker-compose/env.example b/Docker-compose/env.example index f2bd8c584..5bb28cbea 100644 --- a/Docker-compose/env.example +++ b/Docker-compose/env.example @@ -1,5 +1,6 @@ SATOSA_HOSTNAME=localhost SATOSA_BASE=https://${SATOSA_HOSTNAME:-localhost} +SATOSA_BASE_OPENID4VCI=${SATOSA_BASE}/OpenID4VCI SATOSA_BASE_STATIC=${SATOSA_BASE}/static SATOSA_DISCO_SRV=${SATOSA_BASE_STATIC}/disco.html SATOSA_UNKNOW_ERROR_REDIRECT_PAGE=${SATOSA_BASE_STATIC}/error_page.html @@ -63,3 +64,5 @@ SATOSA_UI_PRIVACY_URL_IT="https://example_organization.org/it/privacy" # If set to true, satosa downloads IDEM's keys and IDPs from registry.spid.gov.it GET_IDEM_MDQ_KEY=true + +SATOSA_DEBUG=true \ No newline at end of file diff --git a/Docker-compose/nginx/conf.d/default.conf b/Docker-compose/nginx/conf.d/default.conf index 956b93650..8f1b60eeb 100644 --- a/Docker-compose/nginx/conf.d/default.conf +++ b/Docker-compose/nginx/conf.d/default.conf @@ -41,6 +41,8 @@ server { uwsgi_param SERVER_ADDR $server_addr; } + include ./conf.d/sites-enabled/*.conf; + location /static/ { alias /usr/share/nginx/html/static/; autoindex on; diff --git a/Docker-compose/run-docker-compose.sh b/Docker-compose/run-docker-compose.sh index 49cedd61a..4dc075877 100755 --- a/Docker-compose/run-docker-compose.sh +++ b/Docker-compose/run-docker-compose.sh @@ -11,6 +11,8 @@ function clean_data { rm -Rf ./djangosaml2_sp/* rm -Rf ./nginx/html/static rm -Rf ./certbot/live/localhost/* + rm -Rf ./nginx/conf.d/sites-enabled/* + rm -Rf ./wwwallet/* if [ "$SATOSA_FORCE_ENV" == "true" ]; then rm .env; fi else if [ "$SATOSA_FORCE_ENV" == "true" ]; then echo "'-e' options is skipped. To perform this option is required '-f' too "; fi @@ -18,7 +20,35 @@ function clean_data { } function init_files () { - if [ -f $1 ]; then echo "$2 file is already initialized" ; else $3 ; fi + if [ -f "$1" ]; then echo "$2 file is already initialized" ; else eval "$3" ; fi +} + +function merge_env() { + local env_source="$1" + local template_file="$2" + local target_file="$3" + + local vars=() + while IFS='=' read -r key _; do + [[ -z "$key" || "$key" =~ ^# ]] && continue + vars+=("$key") + done < "$env_source" + + set -a + source "$env_source" + set +a + + env_interpolate() { + while IFS= read -r line; do + eval "echo \"$line\"" + done + } + + env_interpolate < "$template_file" > "$target_file" + + for var in "${vars[@]}"; do + unset "$var" + done } function add_localhost_cert () { @@ -36,21 +66,64 @@ function add_iam_cert () { function initialize_satosa { echo "WARNING: creating directories with read/write/execute permissions to anybody" - + mkdir -p ./iam-proxy-italia-project mkdir -p ./djangosaml2_sp mkdir -p ./mongo/db mkdir -p ./nginx/html/static mkdir -p ./certbot/live/localhost + mkdir -p ./nginx/conf.d/sites-enabled init_files ./.env ".env" "cp env.example .env" init_files ./iam-proxy-italia-project/proxy_conf.yaml "iam-proxy-italia" "cp -R ../iam-proxy-italia-project ./" init_files ./djangosaml2_sp/run.sh "djangosaml2_sp" "cp -R ../iam-proxy-italia-project_sp/djangosaml2_sp ./" init_files ./nginx/html/static/disco.html "static pages" "cp -R ../iam-proxy-italia-project/static ./nginx/html" - init_files ./certbot/live/localhost/privkey.pem "Locahost cert" "add_localhost_cert" + init_files ./certbot/live/localhost/privkey.pem "Localhost cert" "add_localhost_cert" init_files ./iam-proxy-italia-project/pki/privkey.pem "IAM Proxy cert" "add_iam_cert" rm -Rf ./iam-proxy-italia-project/static + rm -Rf ./iam-proxy-italia-project/wwwallet + + if [ "$COMPOSE_PROFILES" == *"wwwallet"* ]; then + mkdir -p ./wwwallet + + init_files "./nginx/conf.d/sites-enabled/wwwallet.conf" \ + "nginx wwwallet configuration is already initialized" \ + "cp -R ../iam-proxy-italia-project/wwwallet/configs/wwwallet.conf ./nginx/conf.d/sites-enabled/" + + init_files "./wwwallet/wallet-frontend/package.json" \ + "wwwallet-frontend directory is already initialized" \ + "cp -R ../iam-proxy-italia-project/wwwallet/wallet-frontend ./wwwallet/wallet-frontend" + + init_files "./wwwallet/wallet-backend-server/package.json" \ + "wwwallet-backend-server directory is already initialized" \ + "cp -R ../iam-proxy-italia-project/wwwallet/wallet-backend-server ./wwwallet/wallet-backend-server" + + init_files "./wwwallet/wallet-frontend/lib/wallet-common/package.json" \ + "wwwallet-frontend wallet-common directory is already initialized" \ + "mkdir -p ./wwwallet/wallet-frontend/lib/wallet-common && cp -R ../iam-proxy-italia-project/wwwallet/wallet-common/* ./wwwallet/wallet-frontend/lib/wallet-common/" + + init_files "./nginx/conf.d/wwwallet.default.conf" \ + "wwwallet nginx config is already initialized" \ + "cp -R ../iam-proxy-italia-project/wwwallet/configs/nginx/wwwallet.default.conf ./nginx/conf.d/wwwallet.default.conf" + + merge_env ./.env ../iam-proxy-italia-project/wwwallet/configs/.env.prod ./wwwallet/wallet-frontend/.env.prod + cp -R ../iam-proxy-italia-project/wwwallet/configs/config.template.ts ./wwwallet/wallet-backend-server/config/config.template.ts + cp -R ../iam-proxy-italia-project/wwwallet/configs/vite.config.ts ./wwwallet/wallet-frontend/vite.config.ts + + mkdir -p ./wwwallet/wallet-backend-server/src/routers && + cp -R ../iam-proxy-italia-project/wwwallet/configs/proxy.router.ts ./wwwallet/wallet-backend-server/src/routers/proxy.router.ts + + mkdir -p ./wwwallet/mysql/config && + cp -R ../iam-proxy-italia-project/wwwallet/mysql/config/my.cnf ./wwwallet/mysql/config/my.cnf + + cp ../iam-proxy-italia-project/wwwallet/configs/openid4vci_frontend.yaml ./iam-proxy-italia-project/conf/frontends/openid4vci_frontend.yaml + + mkdir -p ./wwwallet/mariadb/data + chmod -R 777 ./wwwallet + + echo "WARNING: wwwallet permission folder set recursively to 777" + fi chmod -R 777 ./iam-proxy-italia-project echo "WARNING: iam-proxy-italia-project permission folder set recursively to 777" @@ -115,13 +188,14 @@ function help { echo "-s Skip docker image update" echo "-d Set 'dev' compose profile. Run: satosa, nginx, django-sp, spid-saml-check" echo "-t Run spid_sp_test tests after startup" + echo "-w Set 'wwwallet' compose profile. Run: wwwallet-mariadb, wwwallet-server, wwwallet-frontend" echo "" echo "if isn't set any options of -p, -m, -M, -d, is used 'demo' compose profile" echo "demo compose profile start: satosa, nginx, mongo, mongo-express, django-sp, spid-saml-check" echo "" } -while getopts ":fepbimMdsh" opt; do +while getopts ":fepbimMdswh" opt; do case ${opt} in f) SATOSA_CLEAN_DATA="true" @@ -150,6 +224,9 @@ while getopts ":fepbimMdsh" opt; do t) RUN_SPID_TEST=true ;; + w) + COMPOSE_PROFILES="wwwallet" + ;; h) help exit 0 diff --git a/Docker-compose/stop-docker-compose.sh b/Docker-compose/stop-docker-compose.sh index cb64b67b6..dd6f9f92d 100755 --- a/Docker-compose/stop-docker-compose.sh +++ b/Docker-compose/stop-docker-compose.sh @@ -1,4 +1,7 @@ #!/bin/bash + +export COMPOSE_PROFILES="*" + function help { echo "" echo "### stop-docker-compose.sh" @@ -9,22 +12,28 @@ function help { echo "-a remove all builded after down, like -i, -d" echo "-d remove django-so image after down" echo "-i remove iam-proxy-italia image after down" + echo "-w remove wwwallet images after down" echo "-h print this help" echo "" } function remove_image () { if [ "$1" == "true" ]; then - echo -e "Remove $2 docker image" - docker image rm $2 + if docker image inspect "$2" > /dev/null 2>&1; then + echo -e "Remove $2 docker image" + docker image rm "$2" + else + echo -e "Docker image $2 does not exist, skipping removal" + fi fi } -while getopts ":adhi" opt; do +while getopts ":adhiw" opt; do case ${opt} in a) DJANGO_SP="true" IAM_PROXY_ITALIA="true" + WWWALLET="true" ;; d) DJANGO_SP="true" @@ -36,6 +45,10 @@ while getopts ":adhi" opt; do i) IAM_PROXY_ITALIA="true" ;; + w) + WWWALLET="true" + COMPOSE_PROFILES="wwwallet" + ;; ?) echo "Invalid option: -${OPTARG}." echo "" @@ -48,8 +61,10 @@ done echo -e "\n" echo -e "Eseguo il down della composizione. \n" -docker compose -f docker-compose.yml --profile "*" down -v --remove-orphans +docker compose -f docker-compose.yml --profile $COMPOSE_PROFILES down -v --remove-orphans remove_image "$DJANGO_SP" "docker-compose-django_sp" remove_image "$IAM_PROXY_ITALIA" "iam-proxy-italia" +remove_image "$WWWALLET" "wwwallet-server" +remove_image "$WWWALLET" "wwwallet-frontend" exit 0 diff --git a/Dockerfile b/Dockerfile index 3a6045408..1af037668 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,7 +25,8 @@ RUN addgroup -S satosa && adduser -S satosa -G satosa && chown satosa:satosa $BA # "tzdata" package is required to set timezone with TZ environment # "mailcap" package is required to add mimetype support -RUN apk add --update --no-cache tzdata mailcap xmlsec libffi-dev openssl-dev python3-dev py3-pip openssl build-base gcc wget bash pcre-dev +RUN apk add --update --no-cache tzdata mailcap xmlsec libffi-dev openssl-dev \ + python3-dev py3-pip openssl build-base gcc wget bash pcre-dev gettext # Set up Python virtual environment ENV VIRTUAL_ENV=/.venv diff --git a/docs/readme.wwwallet_setup.md b/docs/readme.wwwallet_setup.md new file mode 100644 index 000000000..d45548454 --- /dev/null +++ b/docs/readme.wwwallet_setup.md @@ -0,0 +1,46 @@ + +# Setup Instructions + +## Prerequisites +Before you begin, ensure you have the following installed: +- Docker +- Docker Compose +- Git + +### WWWAllet Backend Setup +Every aspect of backend configuration is managed through `iam-proxy-italia-project/wwwallet/configs/config.template.ts` file and if you need to customize it, like changing the database connection details or enabling/disabling certain features, you can do so by editing this file. +Note that you will need to set: +- the host and port where the backend will be running. +- the database connection details to connect to your Mysql instance. +- and the notification system need to be disabled if no firebase subscription is available. + +### WWWAllet Frontend Setup +The frontend configuration is managed through the `iam-proxy-italia-project/wwwallet/configs/.env.prod` file. +You can customize it by editing this file. +Note that you will need to set: +- the backend url to connect to the backend instance. +- the firebase configuration if you want to enable the notification system. + +### Nginx Custom Configuration +The Nginx configuration for wwwallet is managed through the `iam-proxy-italia-project/wwwallet/configs/wwwallet.conf` file. +If you change the backend or frontend host and port, you will need to update this file accordingly. + +## Installation Steps + +### Automated Setup with Docker-Compose +The installation process is completely automated by the script `run-docker-compose.sh` located in the `Docker-compose` folder. +You can set the variable `COMPOSE_PROFILES` to the value `wwwallet` into the script and run it with the command: +```bash +./run-docker-compose.sh -w +``` + +### Trusted Issuer Configuration +After the backend initialization, you must add the instance of the OpenID4VCI frontend, distributed in iam-proxy-italia using [pyeudiw](https://github.com/italia/eudi-wallet-it-python), as trusted issuer. +We therefore need to configure the enabled credential issuer by adding an entry in the table `credential_issuer` of the Mysql database used by wwwallet backend. +You can do this with any MariaDB client or using the MariaDB command line. +Note that the url must point to the OpenID4VCI Frontend to work properly. +An example of the SQL command to be executed is the following: +```sql +INSERT INTO wwwalletdb.credential_issuer (clientId,credentialIssuerIdentifier,visible) + VALUES ('Satosa OpenID4VCI','https://localhost/OpenID4VCI',1); +``` \ No newline at end of file diff --git a/iam-proxy-italia-project/conf/backends/pyeudiw_backend.yaml b/iam-proxy-italia-project/conf/backends/pyeudiw_backend.yaml index f25710447..6a2609c32 100644 --- a/iam-proxy-italia-project/conf/backends/pyeudiw_backend.yaml +++ b/iam-proxy-italia-project/conf/backends/pyeudiw_backend.yaml @@ -112,6 +112,9 @@ config: duckle: dcql_query: '{"credentials":[{"id":"personal id data","format":"dc+sd-jwt","meta":{"vct_values":["https://trust-registry.eid-wallet.example.it/credentials/v1.0/personidentificationdata"]},"claims":[{"path":["given_name"]},{"path":["family_name"]}]},{"id":"wallet attestation","format":"mso_mdoc","meta":{"vct_values":["https://itwallet.registry.example.it/WalletAttestation"]},"claims":[{"path":["wallet_link"]},{"path":["wallet_name"]}]}]}' + security: + wallet_attestation_required: false # this is the flag to enable the wallet attestation interoperability, if missing it will be set to true by default + user_attributes: unique_identifiers: - tax_id_code diff --git a/iam-proxy-italia-project/conf/microservices/target_based_routing.yaml b/iam-proxy-italia-project/conf/microservices/target_based_routing.yaml index 79a502be4..85abdd4ae 100644 --- a/iam-proxy-italia-project/conf/microservices/target_based_routing.yaml +++ b/iam-proxy-italia-project/conf/microservices/target_based_routing.yaml @@ -1,7 +1,7 @@ module: satosa.micro_services.custom_routing.DecideBackendByTargetIssuer name: TargetRouter config: - default_backend: Saml2 + default_backend: "spidSaml2" target_mapping: # test platforms @@ -26,4 +26,4 @@ config: "https://idp.intesigroup.com": "spidSaml2" "https://idserver.servizicie.interno.gov.it/idp/profile/SAML2/POST/SSO": "cieSaml2" #IT Wallet - "wallet": "OpenID4VP" + "openid4vp": "OpenID4VP" \ No newline at end of file diff --git a/iam-proxy-italia-project/entrypoint.sh b/iam-proxy-italia-project/entrypoint.sh index f248988bb..29888c251 100755 --- a/iam-proxy-italia-project/entrypoint.sh +++ b/iam-proxy-italia-project/entrypoint.sh @@ -3,15 +3,15 @@ ### function get_data ### # try 3 time to get remote http/https data and copy to destination if third param is set "true" -# each try have 2 second of timeout, on error the destination file it is not written. Require wget. +# each try have 2 second of timeout, on error the destination file it is not written. Require wget. # # get_data origin destination param_to_test function get_data { if [[ $3 == 'true' ]]; then - TMP=`mktemp` + TMP=$(mktemp) wget $1 -nv -t3 -T2 -O $TMP && cp $TMP $2 rm $TMP - unset $tmp + unset TMP chmod +r $2 fi } @@ -22,8 +22,22 @@ get_data https://registry.spid.gov.it/entities-idp ./metadata/idp/spid-entities- get_data https://sp-proxy.eid.gov.it/metadata ./metadata/idp/ficep.xml $SATOSA_GET_FICEP_IDP_METADATA get_data https://idserver.servizicie.interno.gov.it/idp/shibboleth?Metadata ./metadata/idp/cie-production.xml $SATOSA_GET_CIE_IDP_METADATA +### Expand environment variables in all YAML files ### +CONFIG_DIR=/satosa_proxy/conf +CONFIG_EXTENSIONS="yaml yml" + +echo "Expanding environment variables in all config files..." +for ext in $CONFIG_EXTENSIONS; do + find "$CONFIG_DIR" -type f -name "*.$ext" | while read -r file; do + echo "Processing $file ..." + envsubst < "$file" > "${file}.tmp" && mv "${file}.tmp" "$file" + done +done +echo "All config files processed." + +### Launch SATOSA ### wsgi_file=/.venv/lib/$(python -c 'import sys; print(f"python{sys.version_info.major}.{sys.version_info.minor}")')/site-packages/satosa/wsgi.py -wsgi_cmd="" + if [[ $SATOSA_DEBUG == "true" ]]; then uwsgi --ini /satosa_proxy/uwsgi_setup/uwsgi/uwsgi.ini.debug --wsgi-file $wsgi_file else diff --git a/iam-proxy-italia-project/static/locales/wallets-en.json b/iam-proxy-italia-project/static/locales/wallets-en.json index 197fbfc91..c94288c06 100644 --- a/iam-proxy-italia-project/static/locales/wallets-en.json +++ b/iam-proxy-italia-project/static/locales/wallets-en.json @@ -20,7 +20,7 @@ "name": "IT-Wallet", "logo_text": "Login with IT-Wallet", "logo": "wallet-it/wallet_icon.svg", - "login_url": "https://localhost/Saml2/disco?entityID=wallet", + "login_url": "https://localhost/Saml2/disco?entityID=openid4vp", "learn_more_descr": "IT-Wallet è il Sistema di portafoglio digitale italiano che ti dà il pieno controllo sulle tue informazioni, senza che l'ente che le ha rilasciate venga a conoscenza di quando e come vengono usate." }, "spid": { diff --git a/iam-proxy-italia-project/static/locales/wallets-it.json b/iam-proxy-italia-project/static/locales/wallets-it.json index e53d33d83..8cb3ae08d 100644 --- a/iam-proxy-italia-project/static/locales/wallets-it.json +++ b/iam-proxy-italia-project/static/locales/wallets-it.json @@ -20,7 +20,7 @@ "name": "IT-Wallet", "logo_text": "Entra con IT-Wallet", "logo": "wallet-it/wallet_icon.svg", - "login_url": "https://localhost/Saml2/disco?entityID=wallet", + "login_url": "https://localhost/Saml2/disco?entityID=openid4vp", "learn_more_descr": "IT-Wallet è il Sistema di portafoglio digitale italiano che ti dà il pieno controllo sulle tue informazioni, senza che l'ente che le ha rilasciate venga a conoscenza di quando e come vengono usate." }, "spid": { diff --git a/iam-proxy-italia-project/wwwallet/configs/.env.prod b/iam-proxy-italia-project/wwwallet/configs/.env.prod new file mode 100755 index 000000000..773e84c18 --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/configs/.env.prod @@ -0,0 +1,28 @@ +HOST='0.0.0.0' +PORT=3000 +VITE_WS_URL=wss://${SATOSA_HOSTNAME}/wwwallet-server/ +VITE_WALLET_BACKEND_URL=https://${SATOSA_HOSTNAME}/wwwallet-server +VITE_LOGIN_WITH_PASSWORD=true +VITE_FIREBASE_ENABLED=false +VITE_FIREBASE_VAPIDKEY= +VITE_FIREBASE_API_KEY= +VITE_FIREBASE_AUTH_DOMAIN= +VITE_FIREBASE_PROJECT_ID= +VITE_FIREBASE_STORAGE_BUCKET= +VITE_FIREBASE_MESSAGING_SENDER_ID= +VITE_FIREBASE_APP_ID= +VITE_FIREBASE_MEASUREMENT_ID= +VITE_DID_KEY_VERSION=jwk_jcs-pub +VITE_APP_VERSION=$npm_package_version +VITE_GENERATE_SOURCEMAP=false +VITE_DISPLAY_CONSOLE=true +VITE_WEBAUTHN_RPID=${SATOSA_HOSTNAME} +VITE_OPENID4VCI_REDIRECT_URI=https://${SATOSA_HOSTNAME}/wallet-frontend +VITE_OPENID4VCI_PROOF_TYPE_PRECEDENCE="attestation,jwt" +VITE_OPENID4VP_SAN_DNS_CHECK=false +VITE_OPENID4VP_SAN_DNS_CHECK_SSL_CERTS=false +VITE_VALIDATE_CREDENTIALS_WITH_TRUST_ANCHORS=true +VITE_MULTI_LANGUAGE_DISPLAY=true +VITE_STATIC_PUBLIC_URL=https://demo.wwwallet.org +VITE_STATIC_NAME=wwWallet +VITE_DISPLAY_ISSUANCE_WARNINGS=false diff --git a/iam-proxy-italia-project/wwwallet/configs/config.template.ts b/iam-proxy-italia-project/wwwallet/configs/config.template.ts new file mode 100755 index 000000000..eb94befe1 --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/configs/config.template.ts @@ -0,0 +1,27 @@ +export const config = { + url: "localhost", + port: "8002", + appSecret: "SERVICE_SECRET", + ssl: false, + db: { + host: "wwwallet-mariadb", + port: "3306", + username: "root", + password: "changeme", + dbname: "wwwalletdb" + }, + walletClientUrl: "WALLET_CLIENT_URL", + webauthn: { + attestation: "direct", + origin: "WEBAUTHN_ORIGIN", + rp: { + id: "WEBAUTHN_RP_ID", + name: "wwWallet demo", + }, + }, + alg: "EdDSA", + notifications: { + enabled: false, + serviceAccount: "firebaseConfig.json" + } +} diff --git a/iam-proxy-italia-project/wwwallet/configs/nginx/wwwallet.default.conf b/iam-proxy-italia-project/wwwallet/configs/nginx/wwwallet.default.conf new file mode 100644 index 000000000..e574d7cc6 --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/configs/nginx/wwwallet.default.conf @@ -0,0 +1,59 @@ +server { + listen 443 ssl; + server_name $NGINX_HOST; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_certificate /etc/nginx/certs/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/privkey.pem; + + ssl_ciphers HIGH:!aNULL:!MD5; + + client_max_body_size 10m; + large_client_header_buffers 4 16k; + + add_header X-Frame-Options "DENY"; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Robots-Tag none; + + root /usr/share/nginx/html; + + # Static files + location /static/ { + alias /usr/share/nginx/html/static/; + autoindex on; + } + + # OpenID4VCI requests pass to iam-proxy-italia via uwsgi + location /OpenID4VCI/ { + include /etc/nginx/uwsgi_params; + uwsgi_pass iam-proxy-italia:10000; + + uwsgi_param Host $host; + uwsgi_param X-Real-IP $remote_addr; + uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for; + uwsgi_param X-Forwarded-Proto https; + uwsgi_param HTTP_X_FORWARDED_PROTOCOL https; + + uwsgi_connect_timeout 75s; + uwsgi_read_timeout 40s; + uwsgi_buffer_size 128k; + uwsgi_buffers 4 256k; + uwsgi_busy_buffers_size 256k; + } + + # Error pages + error_page 404 /404.html; + location = /404.html { + root /usr/share/nginx/html/errors; + } + + error_page 403 /403.html; + location = /403.html { + root /usr/share/nginx/html/errors; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html/errors; + } +} \ No newline at end of file diff --git a/iam-proxy-italia-project/wwwallet/configs/openid4vci_frontend.yaml b/iam-proxy-italia-project/wwwallet/configs/openid4vci_frontend.yaml new file mode 100644 index 000000000..051f3d5cb --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/configs/openid4vci_frontend.yaml @@ -0,0 +1,735 @@ +module: pyeudiw.satosa.frontends.openid4vci.openid4vci.OpenID4VCIFrontend +name: OpenID4VCI + +config: + + default_target_authentication_backend: spidSaml2 + + endpoints: + par: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.pushed_authorization_request_endpoint + class: ParHandler + path: '/par' + credential_offer: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.credential_offer_endpoint + class: CredentialOfferHandler + path: '/credential' + credential_offer_qrcode: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.credential_offer_qrcode_endpoint + class: CredentialOfferQrCodeHandler + path: '/credential-qrcode' + authorization: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.authorization_endpoint + class: AuthorizationHandler + path: '/authorization' + token: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.token_endpoint + class: TokenHandler + path: '/token' + nonce: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.nonce_endpoint + class: NonceHandler + path: '/nonce-endpoint' + credential: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.credential_endpoint + class: CredentialHandler + path: '/credential' + # deferred_credential: + # module: pyeudiw.satosa.frontends.openid4vci.endpoints.deferred_credential_endpoint + # class: DeferredCredentialHandler + # path: '/deferred-credential' + # notification: + # module: pyeudiw.satosa.frontends.openid4vci.endpoints.notification_endpoint + # class: NotificationHandler + # path: '/notification' + credential_issuer_metadata: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.credential_issuer_metadata_endpoint + class: CredentialIssuerMetadataHandler + path: '.well-known/openid-credential-issuer' + oauth_authorization_server_metadata: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.oauth_authorization_server_metadata_endpoint + class: OauthAuthorizationServerMetadataHandler + path: '.well-known/oauth-authorization-server' + metadata: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.metadata_endpoint + class: MetadataHandler + path: '/.well-known/openid-federation' + status_list: + module: pyeudiw.satosa.frontends.openid4vci.endpoints.status_list_endpoint + class: StatusListHandler + path: &status_list_endpoint_path '/status' + + qrcode: + ui: + static_storage_url: !ENV SATOSA_BASE_STATIC + template_folder: "templates" # project root + qrcode_template: "qr_code.html" + authorization_error_template: "authorization_error.html" + size: 250 # px + color: '#000000' # hex + expiration_time: 120 # seconds + logo_path: 'wallet-it/wallet-icon-blue.svg' # relative to static_storage_url + + network: + httpc_params: &httpc_params + connection: + ssl: true + session: + timeout: 6 + + jwt: + default_sig_alg: ES256 # or RS256. Please note that this signature alg MUST be compliant with the private keys used for the signature. X.509 certificates MUST be therefore ECDSA using ES, and RSA using RS + default_enc_alg: RSA-OAEP + default_enc_enc: A256CBC-HS512 + default_exp: 6 # minutes + enc_alg_supported: + - RSA-OAEP + - RSA-OAEP-256 + - ECDH-ES + - ECDH-ES+A128KW + - ECDH-ES+A192KW + - ECDH-ES+A256KW + enc_enc_supported: + - A128CBC-HS256 + - A192CBC-HS384 + - A256CBC-HS512 + - A128GCM + - A192GCM + - A256GCM + sig_alg_supported: + - RS256 + - RS384 + - RS512 + - ES256 + - ES384 + - ES512 + access_token_exp: 90 # mandatory expiration time, in minutes, for the JWT access token returned by the `token` endpoint + refresh_token_exp: 120 # mandatory expiration time, in minutes, for the JWT refresh token returned by the `token` endpoint + par_exp: 90 # mandatory expiration time, in minutes, for the JWT token returned by the `par` endpoint + + # private jwk + # WARNING: change these keys in production env. Keep these keys unchanged can pose a serious security risk. + metadata_jwks: &metadata_jwks + - kty: EC # Please note: this is the first key [0] and it is used for signing the presentation requests as mso_mdoc too + d: YbMCJU43_GkbjUlWwTA5LbVvRRmz4788-k4zl2mjwrE + use: sig + crv: P-256 + kid: 2uhBmKZqkmLaPdJjvQ6ll6dsWXr9FGlouTnhg3mUec0 + x: o8I43oRYmVk5x6Zmq2_Ni--cHD5S81qTD_5cQum2Atk + y: RrhbyuxVw6ZXEpbb8H_HyvEyL7rX0UeSZqcvulHuyFQ + alg: ES256 + + #This is the configuration for the relaying party metadata + metadata: + oauth_authorization_server: + issuer: ${SATOSA_BASE_OPENID4VCI} # this field if not set will be autopopulated using internal variables base_url and name using the following format: "/" + pushed_authorization_request_endpoint: ${SATOSA_BASE_OPENID4VCI}/par + authorization_endpoint: ${SATOSA_BASE_OPENID4VCI}/OpenID4VCI/authorization + token_endpoint: ${SATOSA_BASE_OPENID4VCI}/token + client_registration_types_supported: + - automatic + code_challenge_methods_supported: + - S256 + acr_values_supported: + - https://trust-registry.eid-wallet.example.it/loa/substantial + - https://trust-registry.eid-wallet.example.it/loa/high + scopes_supported: + - EuropeanDisabilityCard + - mDL + response_modes_supported: + - form_post.jwt + - query + response_types_supported: + - code + authorization_signing_alg_values_supported: + - ES256 + - ES384 + - ES512 + grant_types_supported: + - authorization_code + token_endpoint_auth_methods_supported: + - attest_jwt_client_auth + token_endpoint_auth_signing_alg_values_supported: + - ES256 + - ES384 + - ES512 + request_object_signing_alg_values_supported: + - ES256 + - ES384 + - ES512 + dpop_signing_alg_values_supported: # Optional supported algorithm for validate DPoP, if missing every value in cnf is admitted. + - ES256 + - ES384 + - ES512 + jwks: *metadata_jwks + openid_credential_issuer: &openid_credential_issuer_metadata + credential_issuer: ${SATOSA_BASE_OPENID4VCI} # this field if not set will be autopopulated using internal variables base_url and name using the following format: "/" + credential_endpoint: /credential + nonce_endpoint: /nonce-endpoint + deferred_credential_endpoint: /deferred-credential + revocation_endpoint: /revoke + status_assertion_endpoint: /status + notification_endpoint: /notification + credential_hash_alg_supported: sha-256 + display: + - name: EAA Provider + locale: it-IT + - name: EAA Provider + locale: en-US + credential_configurations_supported: + dc_sd_jwt_EuropeanDisabilityCard: + format: dc+sd-jwt + scope: EuropeanDisabilityCard + cryptographic_binding_methods_supported: + - jwk + credential_signing_alg_values_supported: + - ES256 + - ES384 + - ES512 + proof_types_supported: + jwt: + proof_signing_alg_values_supported: + - ES256 + - ES384 + - ES512 + display: + - name: Carta della disabilità europea + locale: it-IT + - name: European Disability Card + locale: en-US + vct: https://trust-registry.eid-wallet.example.it/v1.0/EuropeanDisabilityCard + claims: + - path: + - document_number + display: + - name: Numero Documento + locale: it-IT + - name: Document Number + locale: en-US + - path: + - given_name + display: + - name: Nome + locale: it-IT + - name: Name + locale: en-US + - path: + - family_name + display: + - name: Cognome + locale: it-IT + - name: Family Name + locale: en-US + - path: + - birth_date + display: + - name: Data di Nascita (YYYY-MM-GG) + locale: it-IT + - name: Date of Birth (YYYY-MM-GG) + locale: en-US + - path: + - personal_administrative_number + display: + - name: Codice Fiscale + locale: it-IT + - name: Tax Identification Number + locale: en-US + - path: + - expiry_date + display: + - name: Data di Scadenza (YYYY-MM-GG) + locale: it-IT + - name: Expiration Date (YYYY-MM-GG) + locale: en-US + - path: + - constant_attendance_allowance + display: + - name: Diritto accompagnatore + locale: it-IT + - name: Constant attendance allowance + locale: en-US + - path: + - portrait + display: + - name: Foto codificata in base64 + locale: it-IT + - name: Portrait base64 encoded + locale: en-US + - path: + - link_qr_code + display: + - name: Link QR Code + locale: it-IT + - name: Link QR Code + locale: en-US + dc_sd_jwt_mDL: + format: dc+sd-jwt + scope: mDL + cryptographic_binding_methods_supported: + - jwk + credential_signing_alg_values_supported: + - ES256 + - ES384 + - ES512 + proof_types_supported: + jwt: + proof_signing_alg_values_supported: + - ES256 + - ES384 + - ES512 + display: + - name: Patente di guida + locale: it-IT + - name: Mobile Driver's License + locale: en-US + vct: https://trust-registry.eid-wallet.example.it/v1.0/mDL + claims: + - path: + - given_name + display: + - name: Nome + locale: it-IT + - name: Name + locale: en-US + - path: + - family_name + display: + - name: Cognome + locale: it-IT + - name: Family Name + locale: en-US + - path: + - birth_date + display: + - name: Data di Nascita (YYYY-MM-GG) + locale: it-IT + - name: Date of Birth (YYYY-MM-GG) + locale: en-US + - path: + - place_of_birth + display: + - name: Luogo di Nascita + locale: it-IT + - name: Place of Birth + locale: en-US + - path: + - issue_date + display: + - name: Data di rilascio (YYYY-MM-GG) + locale: it-IT + - name: Issue Date (YYYY-MM-GG) + locale: en-US + - path: + - expiry_date + display: + - name: Data di Scadenza (YYYY-MM-GG) + locale: it-IT + - name: Expiration Date (YYYY-MM-GG) + locale: en-US + - path: + - issuing_country + display: + - name: Paese di rilascio + locale: it-IT + - name: Issuing Country + locale: en-US + - path: + - issuing_authority + display: + - name: Autorità di rilascio + locale: it-IT + - name: Issuing Authority + locale: en-US + - path: + - document_number + display: + - name: Numero Documento + locale: it-IT + - name: Document Number + locale: en-US + - path: + - portrait + display: + - name: Foto codificata in base64 + locale: it-IT + - name: Portrait base64 encoded + locale: en-US + - path: + - driving_privileges + display: + - name: Elenco delle categorie di abilitazione separate da spazio + locale: it-IT + - name: Driving Privileges separated by space + locale: en-US + - path: + - restrictions_conditions + display: + - name: Annotazioni/Restrizioni valide per tutte le categorie separate da spazio + locale: it-IT + - name: Restriction/Condition for all driving privileges separated by space + locale: en-US + - path: + - driving_privileges_details + display: + - name: Dettagli delle categorie di abilitazione + locale: it-IT + - name: Driving privilege details + locale: en-US + mso_mdoc_mDL: + format: mso_mdoc + scope: mDL + doctype: org.iso.18013.5.1.mDL + cryptographic_binding_methods_supported: + - cose_key + credential_signing_alg_values_supported: + - ES256 + - ES384 + - ES512 + display: + - name: Patente di guida + locale: it-IT + - name: Mobile Driver's License + locale: en-US + claims: + - path: + - org.iso.18013.5.1 + - given_name + display: + - name: Nome + locale: it-IT + - name: First Name + locale: en-US + - path: + - org.iso.18013.5.1 + - family_name + display: + - name: Cognome + locale: it-IT + - name: Family Name + locale: en-US + - path: + - org.iso.18013.5.1 + - birth_date + display: + - name: Data di nascita (YYYY-MM-GG) + locale: it-IT + - name: Date of Birth (YYYY-MM-GG) + locale: en-US + - path: + - org.iso.18013.5.1 + - birth_place + display: + - name: Luogo di Nascita + locale: it-IT + - name: Place of Birth + locale: en-US + - path: + - org.iso.18013.5.1 + - issue_date + display: + - name: Data di rilascio (YYYY-MM-GG) + locale: it-IT + - name: Issue Date (YYYY-MM-GG) + locale: en-US + - path: + - org.iso.18013.5.1 + - expiry_date + display: + - name: Data di scadenza (YYYY-MM-GG) + locale: it-IT + - name: Expiry Date (YYYY-MM-GG) + locale: en-US + - path: + - org.iso.18013.5.1 + - issuing_country + display: + - name: Paese di rilascio + locale: it-IT + - name: Issuing Country + locale: en-US + - path: + - org.iso.18013.5.1 + - issuing_authority + display: + - name: Autorità di rilascio + locale: it-IT + - name: Issuing Authority + locale: en-US + - path: + - org.iso.18013.5.1 + - document_number + display: + - name: Numero di documento + locale: it-IT + - name: Document Number + locale: en-US + - path: + - org.iso.18013.5.1 + - portrait + display: + - name: Foto codificata in base64 + locale: it-IT + - name: Portrait base64 encoded + locale: en-US + - path: + - org.iso.18013.5.1 + - driving_privileges + display: + - name: Elenco delle categorie di abilitazione e relativi dettagli su restrizioni/condizioni + locale: it-IT + - name: Driving Privileges and related restrictions/conditions details + locale: en-US + - path: + - org.iso.18013.5.1 + - un_distinguishing_sign + display: + - name: Codice identificativo della Nazione + locale: it-IT + - name: Distinguishing sign of the issuing country + locale: en-US + jwks: *metadata_jwks + trust_frameworks_supported: + - it_cie + - it_wallet + - eudi_wallet + evidence_supported: + - vouch + federation_entity: &federation_entity_metadata + organization_name: Organization Name + homepage_uri: ${SATOSA_BASE_OPENID4VCI}/ + policy_uri: ${SATOSA_BASE_OPENID4VCI}/public/privacy_policy.html + tos_uri: ${SATOSA_BASE_OPENID4VCI}/public/info_policy.html + logo_uri: ${SATOSA_BASE_OPENID4VCI}/public/logo.svg + contacts: + - informazioni@example.it + - protocollo@pec.example.it + + security: + wallet_attestation_required: false # this is the flag to enable the wallet attestation interoperability, if missing it will be set to true by default + signed_par_request: 'false' # this is the flag to enable the PAR request signing, if missing it will be set to true by default + + # this is the custom configuration for credential management. + credential_configurations: + lookup_source: openid4vci #this key is the same one found in `internal_attributes.yaml`, used for lookup filter for attributes + status_list: + path: *status_list_endpoint_path + exp: 90 # minutes + ttl: 43200 + credential_specification: + mso_mdoc_mDL: + expiry_days: 365 + template: | + org.iso.18013.5.1: + family_name: "{{surname}}" + given_name: "{{name}}" + birth_date: "{{dateOfBirth}}" + expiry_date: "{{expiryDate}}" + issue_date: "{{issueDate}}" + issuing_country: "{{issuingCountry}}" + issuing_authority: "{{issuingAuthority}}" + document_number: "{{documentNumber}}" + un_distinguishing_sign: "I" + portrait: !!binary "{{portrait_b64}}" + driving_privileges: "{{driving_privileges}}" + org.iso.18013.5.1.it: + verification.evidence: + organization_name: "{{verification.organization_name}}" + organization_id: "{{verification.organization_id}}" + country_code: "{{verification.country_code}}" + verification.trust_framework: "{{trust_framework}}" + verification.assurance_level: "{{assurance_level}}" + dc_sd_jwt_mDL: + template: | + holder_disclosed_claims: + !sd given_name: "{{name}}" + !sd family_name: "{{surname}}" + !sd place_of_birth: + country: "{{countyOfBirth}}" + locality: "{{placeOfBirth}}" + key_binding: true + user_claims: + !sd birthdate: "{{dateOfBirth}}" + !sd family_name: "{{surname}}" + !sd given_name: "{{name}}" + !sd place_of_birth: + country: "{{countyOfBirth}}" + locality: "{{placeOfBirth}}" + !sd tax_id_code: "TINIT-{{fiscal_code}}" + !sd unique_id: "{{unique_id}}" + + + trust: + federation: + module: pyeudiw.trust.handler.federation + class: FederationHandler + config: + httpc_params: *httpc_params + cache_ttl: 0 + entity_configuration_exp: 600 + metadata_type: "openid_credential_issuer" + metadata: *openid_credential_issuer_metadata + authority_hints: + - http://127.0.0.1:8000 + trust_anchors: + - http://127.0.0.1:8000: + - + - https://trust-anchor.edu: + - + - https://trust-anchor.example.org: + # WARNING: change these keys in production env. Keep these keys unchanged can pose a serious security risk. + - kty: EC, + d: WbhdFpNhUTQ7gfh8XUQzMfe8JPVAKAh4qCfHRMqyQE8, + use: sig, + crv: P-256, + kid: 4_6lPScdml0BplAsv8RO0u7B2XnrvusAMpFWNI_QJco, + x: aPZam7vAOLAdx3HOAQM3PJgczLS2Od8uIxW755a0T2c, + y: cUnLFFz8S_STZmDRYOghxMNJobDCtVQLEyeF_VkJl9Y, + alg: ES256 + default_sig_alg: "RS256" + trust_marks: [] + federation_entity_metadata: *federation_entity_metadata + federation_jwks: # !ENV PYEUDIW_FEDERATION_JWKS + - kty: RSA + d: QUZsh1NqvpueootsdSjFQz-BUvxwd3Qnzm5qNb-WeOsvt3rWMEv0Q8CZrla2tndHTJhwioo1U4NuQey7znijhZ177bUwPPxSW1r68dEnL2U74nKwwoYeeMdEXnUfZSPxzs7nY6b7vtyCoA-AjiVYFOlgKNAItspv1HxeyGCLhLYhKvS_YoTdAeLuegETU5D6K1xGQIuw0nS13Icjz79Y8jC10TX4FdZwdX-NmuIEDP5-s95V9DMENtVqJAVE3L-wO-NdDilyjyOmAbntgsCzYVGH9U3W_djh4t3qVFCv3r0S-DA2FD3THvlrFi655L0QHR3gu_Fbj3b9Ybtajpue_Q + e: AQAB + kid: 9Cquk0X-fNPSdePQIgQcQZtD6J0IjIRrFigW2PPK_-w + n: utqtxbs-jnK0cPsV7aRkkZKA9t4S-WSZa3nCZtYIKDpgLnR_qcpeF0diJZvKOqXmj2cXaKFUE-8uHKAHo7BL7T-Rj2x3vGESh7SG1pE0thDGlXj4yNsg0qNvCXtk703L2H3i1UXwx6nq1uFxD2EcOE4a6qDYBI16Zl71TUZktJwmOejoHl16CPWqDLGo9GUSk_MmHOV20m4wXWkB4qbvpWVY8H6b2a0rB1B1YPOs5ZLYarSYZgjDEg6DMtZ4NgiwZ-4N1aaLwyO-GLwt9Vf-NBKwoxeRyD3zWE2FXRFBbhKGksMrCGnFDsNl5JTlPjaM3kYyImE941ggcuc495m-Fw + p: 2zmGXIMCEHPphw778YjVTar1eycih6fFSJ4I4bl1iq167GqO0PjlOx6CZ1-OdBTVU7HfrYRiUK_BnGRdPDn-DQghwwkB79ZdHWL14wXnpB5y-boHz_LxvjsEqXtuQYcIkidOGaMG68XNT1nM4F9a8UKFr5hHYT5_UIQSwsxlRQ0 + q: 2jMFt2iFrdaYabdXuB4QMboVjPvbLA-IVb6_0hSG_-EueGBvgcBxdFGIZaG6kqHqlB7qMsSzdptU0vn6IgmCZnX-Hlt6c5X7JB_q91PZMLTO01pbZ2Bk58GloalCHnw_mjPh0YPviH5jGoWM5RHyl_HDDMI-UeLkzP7ImxGizrM + x509: + module: pyeudiw.trust.handler.x509 + class: X509Handler + config: + issuer_id: "x509_san_dns:localhost" + certificate_authorities: + ca.example.com: | + -----BEGIN CERTIFICATE----- + MIICMTCCAdegAwIBAgIUFz6Jqc1G5/Ykbl6cM5VGZ+LOnRIwCgYIKoZIzj0EAwIw + UjEuMCwGA1UEAwwlQ049Y2EuZXhhbXBsZS5jb20sIE89RXhhbXBsZSBDQSwgQz1J + VDETMBEGA1UECgwKRXhhbXBsZSBDQTELMAkGA1UEBhMCSVQwHhcNMjUwNzAyMTY0 + NDQwWhcNMjYwNzAzMTY0NDQwWjBSMS4wLAYDVQQDDCVDTj1jYS5leGFtcGxlLmNv + bSwgTz1FeGFtcGxlIENBLCBDPUlUMRMwEQYDVQQKDApFeGFtcGxlIENBMQswCQYD + VQQGEwJJVDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPomtRSvN+6K9ld74iPp + 5YWy7Cv2jWcQ7qlx8AnFl9fCEx8qVK0yW0PUp+tDXE7PSELY7YgXnMIyU7xzR0n0 + fJejgYowgYcwEgYDVR0TAQH/BAgwBgEB/wIBATAuBgNVHR8EJzAlMCOgIaAfhh1o + dHRwOi8vY2EuZXhhbXBsZS5jb20vY3JsLnBlbTAOBgNVHQ8BAf8EBAMCAaYwMQYD + VR0RBCowKIYWaHR0cHM6Ly9jYS5leGFtcGxlLmNvbYIOY2EuZXhhbXBsZS5jb20w + CgYIKoZIzj0EAwIDSAAwRQIhAPpXUeGOprVYUqNZX0gC4dbXSUqeTeaqt3WHyBVl + wLulAiA3ZUIZHwa0P+xT1TxnED6udHf9+yyFQQEntrw7NdzveQ== + -----END CERTIFICATE----- + leaf_certificate_chains_by_ca: # X.509 chains in PEM format. Please note: Leaf's certificate MUST be related to metadata_jwks[0] + ca.example.com: + - | + -----BEGIN CERTIFICATE----- + MIICdzCCAh2gAwIBAgIUVYrg1ljjjT83r6pbjHi2/j5gkY0wCgYIKoZIzj0EAwIw + TzEhMB8GA1UEAwwYaW50ZXJtZWRpYXRlLmV4YW1wbGUuY29tMR0wGwYDVQQKDBRF + eGFtcGxlIEludGVybWVkaWF0ZTELMAkGA1UEBhMCSVQwHhcNMjUwNzAyMTY0NDQw + WhcNMjYwNzAzMTY0NDQwWjBYMTIwMAYDVQQDDClDTj1sZWFmLmV4YW1wbGUuY29t + LCBPPUV4YW1wbGUgTGVhZiwgQz1JVDEVMBMGA1UECgwMRXhhbXBsZSBMZWFmMQsw + CQYDVQQGEwJJVDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKPCON6EWJlZOcem + ZqtvzYvvnBw+UvNakw/+XELptgLZRrhbyuxVw6ZXEpbb8H/HyvEyL7rX0UeSZqcv + ulHuyFSjgc0wgcowDAYDVR0TAQH/BAIwADBCBgNVHR8EOzA5MDegNaAzhjFodHRw + czovL2xlYWYuZXhhbXBsZS5jb20vY3JsL2xlYWYuZXhhbXBsZS5jb20uY3JsMD4G + A1UdHgEB/wQ0MDKgMDAahhhodHRwczovL2xlYWYuZXhhbXBsZS5jb20wEoIQbGVh + Zi5leGFtcGxlLmNvbTAOBgNVHQ8BAf8EBAMCAaYwJgYDVR0RBB8wHYYQbGVhZi5l + eGFtcGxlLm9yZ4IJbG9jYWxob3N0MAoGCCqGSM49BAMCA0gAMEUCIQDHGzkyoZqw + /+fXJOpZ3CfRarm/oYnvFyh/bRbh+AqgrgIgZvS4ThK4NNIs/h2iffGM13xpmIzq + 6lqkCyBfqz25Sj4= + -----END CERTIFICATE----- + - | + -----BEGIN CERTIFICATE----- + MIICZjCCAgygAwIBAgIUdkS8NW1gb+EpEf4TfGo9gnug22UwCgYIKoZIzj0EAwIw + UjEuMCwGA1UEAwwlQ049Y2EuZXhhbXBsZS5jb20sIE89RXhhbXBsZSBDQSwgQz1J + VDETMBEGA1UECgwKRXhhbXBsZSBDQTELMAkGA1UEBhMCSVQwHhcNMjUwNzAyMTY0 + NDQwWhcNMjYwNzAzMTY0NDQwWjBPMSEwHwYDVQQDDBhpbnRlcm1lZGlhdGUuZXhh + bXBsZS5jb20xHTAbBgNVBAoMFEV4YW1wbGUgSW50ZXJtZWRpYXRlMQswCQYDVQQG + EwJJVDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKByvNynPuIVdmOmguNHc7mo + a/LWrobhDg71tvJNrDS+Jlf4LCZUgdUUF0SR9xx63QoKM1gV8x4Dd+rWMtZH5kmj + gcIwgb8wEgYDVR0TAQH/BAgwBgEB/wIBADBSBgNVHR8ESzBJMEegRaBDhkFodHRw + czovL2ludGVybWVkaWF0ZS5leGFtcGxlLm5ldC9jcmwvaW50ZXJtZWRpYXRlLmV4 + YW1wbGUubmV0LmNybDAOBgNVHQ8BAf8EBAMCAaYwRQYDVR0RBD4wPIYgaHR0cHM6 + Ly9pbnRlcm1lZGlhdGUuZXhhbXBsZS5jb22CGGludGVybWVkaWF0ZS5leGFtcGxl + LmNvbTAKBggqhkjOPQQDAgNIADBFAiEApaJ9yW166NquLjChDYUTjvgV47cWYWoz + H0dfJuT0bKACIDJA0vvf7+hIHnUHMX6q3ajvxe38x/SdY/sIB15TQ7PD + -----END CERTIFICATE----- + - | + -----BEGIN CERTIFICATE----- + MIICMTCCAdegAwIBAgIUFz6Jqc1G5/Ykbl6cM5VGZ+LOnRIwCgYIKoZIzj0EAwIw + UjEuMCwGA1UEAwwlQ049Y2EuZXhhbXBsZS5jb20sIE89RXhhbXBsZSBDQSwgQz1J + VDETMBEGA1UECgwKRXhhbXBsZSBDQTELMAkGA1UEBhMCSVQwHhcNMjUwNzAyMTY0 + NDQwWhcNMjYwNzAzMTY0NDQwWjBSMS4wLAYDVQQDDCVDTj1jYS5leGFtcGxlLmNv + bSwgTz1FeGFtcGxlIENBLCBDPUlUMRMwEQYDVQQKDApFeGFtcGxlIENBMQswCQYD + VQQGEwJJVDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPomtRSvN+6K9ld74iPp + 5YWy7Cv2jWcQ7qlx8AnFl9fCEx8qVK0yW0PUp+tDXE7PSELY7YgXnMIyU7xzR0n0 + fJejgYowgYcwEgYDVR0TAQH/BAgwBgEB/wIBATAuBgNVHR8EJzAlMCOgIaAfhh1o + dHRwOi8vY2EuZXhhbXBsZS5jb20vY3JsLnBlbTAOBgNVHQ8BAf8EBAMCAaYwMQYD + VR0RBCowKIYWaHR0cHM6Ly9jYS5leGFtcGxlLmNvbYIOY2EuZXhhbXBsZS5jb20w + CgYIKoZIzj0EAwIDSAAwRQIhAPpXUeGOprVYUqNZX0gC4dbXSUqeTeaqt3WHyBVl + wLulAiA3ZUIZHwa0P+xT1TxnED6udHf9+yyFQQEntrw7NdzveQ== + -----END CERTIFICATE----- + + private_keys: *metadata_jwks # the keys must be the same because the key at position 0 must correspond to the one from which the certificate at position 0 of the chain `leaf_certificate_chains_by_ca` is derived + + # Mongodb database configuration + # Configuration for retrieving user datas, it stores user attributes required to + # generate the requested credential with `credential` endpoint. + user_storage: + mongo_db: + storage: + module: pyeudiw.storage.user_storage + class: UserStorage + init_params: + url: mongodb://satosa-mongo:27017 + conf: + db_name: pyeudiw_openid4vci + db_users_collection: users + data_ttl: 63072000 # 2 years + connection_params: + username: !ENV MONGODB_USERNAME + password: !ENV MONGODB_PASSWORD + # Configuration for retrieving credentials data, it's a storage system used by both + # the `credential` and `status_list` endpoints. It maintains the state of each credential in relation to the corresponding + # user, supporting status updates such as issuance and revocation. + credential_storage: + mongo_db: + storage: + module: pyeudiw.storage.credential_storage + class: CredentialStorage + init_params: + url: mongodb://satosa-mongo:27017 + conf: + db_name: pyeudiw_openid4vci + db_credentials_collection: credentials + data_ttl: 63072000 # 2 years + connection_params: + username: !ENV MONGODB_USERNAME + password: !ENV MONGODB_PASSWORD + + # Mongodb database configuration + storage: + mongo_db: + cache: + module: pyeudiw.storage.mongo_cache + class: MongoCache + init_params: + url: mongodb://satosa-mongo:27017 + conf: + db_name: pyeudiw_openid4vci + connection_params: + username: !ENV MONGODB_USERNAME + password: !ENV MONGODB_PASSWORD + storage: + module: pyeudiw.storage.mongo_storage + class: MongoStorage + init_params: + url: mongodb://satosa-mongo:27017 + conf: + db_name: pyeudiw_openid4vci + db_sessions_collection: sessions + db_trust_attestations_collection: trust_attestations + db_trust_anchors_collection: trust_anchors + db_trust_sources_collection: trust_sources + data_ttl: 63072000 # 2 years + connection_params: + username: !ENV MONGODB_USERNAME + password: !ENV MONGODB_PASSWORD diff --git a/iam-proxy-italia-project/wwwallet/configs/proxy.router.ts b/iam-proxy-italia-project/wwwallet/configs/proxy.router.ts new file mode 100644 index 000000000..c0fe641d2 --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/configs/proxy.router.ts @@ -0,0 +1,59 @@ +import axios from 'axios'; +import express, { Request, Response, Router } from 'express'; +import { Agent } from 'node:https'; +const proxyRouter: Router = express.Router(); + +const agent = new Agent({ + rejectUnauthorized: false, +}); + +proxyRouter.post('/', async (req, res) => { + const { headers, method, url, data } = req.body; + try { + const isBinaryRequest = /\.(png|jpe?g|gif|webp|bmp|tiff?|ico)(\?.*)?(#.*)?$/i.test(url); + console.log("URL = ", url) + const response = await axios({ + url: url, + headers: headers, + method: method, + data: data, + ...(isBinaryRequest && { responseType: 'arraybuffer' }), + maxRedirects: 0, + httpsAgent: agent, + }); + + if (isBinaryRequest) { + // forward all response headers + for (const key in response.headers) { + if (Object.prototype.hasOwnProperty.call(response.headers, key)) { + const value = response.headers[key]; + if (value !== undefined) { + res.setHeader(key, value as string); + } + } + } + return res.status(response.status).send(response.data); + } + + // JSON or other text content + return res.status(response.status).send({ + status: response.status, + headers: response.headers, + data: response.data, + }); + } + catch (err) { + console.error("Error in proxy request: ", err); + if (err.response && err.response.data) { + console.error("Error data = ", err.response.data) + } + if (err.response && err.response.status == 302) { + return res.status(200).send({ status: err.response.status, headers: err.response.headers, data: {} }) + } + return res.status(err.response?.status ?? 104).send({ status: err.response?.status ?? 104, data: err.response?.data, headers: err.response?.headers }); + } +}) + +export { + proxyRouter +} \ No newline at end of file diff --git a/iam-proxy-italia-project/wwwallet/configs/vite.config.ts b/iam-proxy-italia-project/wwwallet/configs/vite.config.ts new file mode 100644 index 000000000..4943bb963 --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/configs/vite.config.ts @@ -0,0 +1,50 @@ +import { defineConfig, loadEnv } from 'vite'; +import react from '@vitejs/plugin-react'; +import svgr from 'vite-plugin-svgr'; +import eslint from 'vite-plugin-eslint'; +import { VitePWA } from 'vite-plugin-pwa'; +import { ManifestPlugin, RobotsTxtPlugin, SitemapPlugin } from './vite-plugins'; + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), '') + return { + base: '/wwwallet-frontend/', + plugins: [ + react(), + svgr(), + eslint(), + ManifestPlugin(env), + RobotsTxtPlugin(env), + SitemapPlugin(env), + VitePWA({ + registerType: 'autoUpdate', + srcDir: 'src', + filename: 'service-worker.js', // Custom service worker (MUST exist in `src/`) + strategies: 'injectManifest', // Uses `src/service-worker.js` for caching + manifest: false, // Vite will use `public/manifest.json` automatically + injectManifest: { + maximumFileSizeToCacheInBytes: 3 * 1024 * 1024, + }, + }), + + ], + resolve: { + alias: { + '@': '/src', + }, + }, + server: { + host: true, + port: 3000, + open: true, + }, + preview: { + host: true, + port: 3000, + open: true, + }, + build: { + sourcemap: env.VITE_GENERATE_SOURCEMAP === 'true', + }, + } +}); diff --git a/iam-proxy-italia-project/wwwallet/configs/wwwallet.conf b/iam-proxy-italia-project/wwwallet/configs/wwwallet.conf new file mode 100644 index 000000000..4e2cc016d --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/configs/wwwallet.conf @@ -0,0 +1,30 @@ +location /wwwallet-frontend/ { + rewrite ^/wwwallet-frontend/(.*)$ /$1 break; + alias /wwwallet-frontend/; + + proxy_pass http://wwwallet-frontend:80/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; # Optional + proxy_set_header X-Forwarded-Port $server_port; +} + + +location /wwwallet-server/ { + rewrite ^/wwwallet-server/(.*)$ /$1 break; + alias /wwwallet-server/; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_pass http://wwwallet-server:8002/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; # Optional + proxy_set_header X-Forwarded-Port $server_port; +} \ No newline at end of file diff --git a/iam-proxy-italia-project/wwwallet/mysql/config/my.cnf b/iam-proxy-italia-project/wwwallet/mysql/config/my.cnf new file mode 100644 index 000000000..a08cf6be1 --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/mysql/config/my.cnf @@ -0,0 +1,2 @@ +[mysqld] +default-authentication-plugin=mysql_native_password \ No newline at end of file diff --git a/iam-proxy-italia-project/wwwallet/wallet-backend-server b/iam-proxy-italia-project/wwwallet/wallet-backend-server new file mode 160000 index 000000000..314e0f248 --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/wallet-backend-server @@ -0,0 +1 @@ +Subproject commit 314e0f248c7eca7332d71aaa4b54b209a5181fa9 diff --git a/iam-proxy-italia-project/wwwallet/wallet-common b/iam-proxy-italia-project/wwwallet/wallet-common new file mode 160000 index 000000000..b3ef992af --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/wallet-common @@ -0,0 +1 @@ +Subproject commit b3ef992af18fb6dac9359e86bef61b04c1ceb6cc diff --git a/iam-proxy-italia-project/wwwallet/wallet-frontend b/iam-proxy-italia-project/wwwallet/wallet-frontend new file mode 160000 index 000000000..2fb65a2f4 --- /dev/null +++ b/iam-proxy-italia-project/wwwallet/wallet-frontend @@ -0,0 +1 @@ +Subproject commit 2fb65a2f4056ae3dd66e5dac5a7c59551665ed01