diff --git a/containers/hydra-dispatcher/Dockerfile b/containers/hydra-dispatcher/Dockerfile new file mode 100644 index 0000000..8ea96ef --- /dev/null +++ b/containers/hydra-dispatcher/Dockerfile @@ -0,0 +1,5 @@ +FROM cadoles/hydra-dispatcher-v1:v0.0.0-111-g2e60bdb + +COPY hydra/providers.yml /var/www/config/hydra/providers.yml + +COPY theme.css /var/www/public/build/theme \ No newline at end of file diff --git a/containers/hydra-dispatcher/hydra/providers.yml b/containers/hydra-dispatcher/hydra/providers.yml new file mode 100644 index 0000000..5a7dccc --- /dev/null +++ b/containers/hydra-dispatcher/hydra/providers.yml @@ -0,0 +1,23 @@ +hydra: + apps: + - id: passwordless + title: + fr: addresse courriel + en: email address + description: + fr: Authentification via adresse courriel + en: Authentication by email address + icon_url: https://upload.wikimedia.org/wikipedia/commons/4/48/You%27ve_got_mail.png + login_url: http://127.0.0.1:7082/login + consent_url: http://127.0.0.1:7082/consent + logout_url: http://127.0.0.1:7082/logout + attributes_rewrite_rules: + username: + - consent.session.id_token.email + email: + - consent.session.id_token.email + email_verified: + - consent.session.id_token.email_verified + + + diff --git a/containers/hydra-dispatcher/theme.css b/containers/hydra-dispatcher/theme.css new file mode 100644 index 0000000..804d4f6 --- /dev/null +++ b/containers/hydra-dispatcher/theme.css @@ -0,0 +1,8 @@ +body > section { + background-color: rgba(214, 170, 214, 0.575); +} + +input[type="radio"]:checked ~ .app-item { + --tw-shadow: 0 10px 15px -3px rgb(22 78 99 / 0.3), 0 4px 6px -4px rgb(22 78 99 / 0.3); + background-color: rgba(251, 255, 21, 0.507); +} diff --git a/containers/hydra/clients.d/nineskeletor.json b/containers/hydra/clients.d/nineskeletor.json new file mode 100644 index 0000000..02c3380 --- /dev/null +++ b/containers/hydra/clients.d/nineskeletor.json @@ -0,0 +1,19 @@ +{ + "client_id": "nineskeletor", + "client_name": "Nineskeletor", + "client_secret": "changeme", + "grant_types": [ + "authorization_code", + "refresh_token" + ], + "jwks": {}, + "metadata": {}, + "token_endpoint_auth_method": "client_secret_post", + "post_logout_redirect_uris": ["https://127.0.0.1:8000"], + "redirect_uris": ["https://127.0.0.1:8000/oauth2/callback"], + "response_types": [ + "code" + ], + "logo_uri": "https://upload.wikimedia.org/wikipedia/commons/e/e1/Password.svg", + "scope": "openid" +} \ No newline at end of file diff --git a/containers/postgresql/init-user-db.sh b/containers/postgresql/init-user-db.sh new file mode 100755 index 0000000..aa665fa --- /dev/null +++ b/containers/postgresql/init-user-db.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e +set -u + +function create_user_and_database() { + local database=$1 + echo " Creating user and database '$database'" + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE USER $database; + CREATE DATABASE $database; + GRANT ALL PRIVILEGES ON DATABASE $database TO $database; +EOSQL +} + +if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then + echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" + for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do + create_user_and_database $db + done + echo "Multiple databases created" +fi diff --git a/docker-compose.yml b/docker-compose.yml index eec3d2a..71a0824 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,20 @@ version: '3' +# Port +# 6379 = redis +# 5432 = postgresql +# 80 = mercure +# 9000 = minio nginx +# 1025 = fake smtp +# 1080 = fake webmail +# 389 = fake ldap +# 636 = fake ldaps +# 6080 = tool phpldapadmin +# 6081 = tool adminer + + services: + # Service redis pour le stockage des sessions redis: image: redis:4.0 container_name: redis @@ -11,25 +25,22 @@ services: volumes: - /etc/localtime:/etc/localtime:ro - postgres: - image: postgres:${POSTGRES_VERSION:-13}-alpine - container_name: postgres + # Service postgresql pour le stockage de la bdd applicative + postgresql: + image: postgres:13-alpine + container_name: postgresql + hostname: postgresql environment: - POSTGRES_DB: ${POSTGRES_DB:-app} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ChangeMe} - POSTGRES_USER: ${POSTGRES_USER:-symfony} + POSTGRES_MULTIPLE_DATABASES: app,hydra + POSTGRES_PASSWORD: ChangeMe + POSTGRES_USER: symfony ports: - 5432:5432 volumes: - db-data:/var/lib/postgresql/data:rw + - ./containers/postgresql:/docker-entrypoint-initdb.d - mailer: - image: schickling/mailcatcher - container_name: mailer - ports: - - 1025:1025 - - 1080:1080 - + # Service websocket mercure: image: dunglas/mercure container_name: mercure @@ -48,38 +59,7 @@ services: - mercure_data:/data - mercure_config:/config - openldap: - image: osixia/openldap:1.5.0 - container_name: openldap - environment: - LDAP_LOG_LEVEL: "256" - LDAP_ORGANISATION: "nineskeletor" - LDAP_DOMAIN: "nine.fr" - LDAP_ADMIN_PASSWORD: "changeme" - LDAP_CONFIG_PASSWORD: "changeme" - LDAP_READONLY_USER: "true" - LDAP_READONLY_USER_USERNAME: "readonly" - LDAP_READONLY_USER_PASSWORD: "readonly" - LDAP_TLS: "false" - volumes: - - /var/lib/ldap - - /etc/ldap/slapd.d - - /container/service/slapd/assets/certs/ - ports: - - "389:389" - - "636:636" - - phpldapadmin: - image: osixia/phpldapadmin:latest - container_name: phpldapadmin - environment: - PHPLDAPADMIN_LDAP_HOSTS: "openldap" - PHPLDAPADMIN_HTTPS: "false" - ports: - - "8080:80" - depends_on: - - openldap - + # Service de stockage Minio minio1: image: minio/minio:RELEASE.2021-01-16T02-19-44Z container_name: minio1 @@ -98,6 +78,7 @@ services: timeout: 20s retries: 3 + # Service de stockage Minio minio2: image: minio/minio:RELEASE.2021-01-16T02-19-44Z container_name: minio2 @@ -116,6 +97,7 @@ services: timeout: 20s retries: 3 + # Service de stockage Minio minio3: image: minio/minio:RELEASE.2021-01-16T02-19-44Z container_name: minio3 @@ -134,6 +116,7 @@ services: timeout: 20s retries: 3 + # Service de stockage Minio minio4: image: minio/minio:RELEASE.2021-01-16T02-19-44Z container_name: minio4 @@ -152,6 +135,7 @@ services: timeout: 20s retries: 3 + # Service nginx orchestrateur des minio nginx: image: nginx:1.19.2-alpine container_name: nginx @@ -164,6 +148,120 @@ services: - minio2 - minio3 - minio4 + + # Service hydra + hydra: + image: cadoles/hydra-v1 + container_name: hydra + volumes: + - ./containers/hydra/clients.d:/etc/hydra/clients.d + ports: + - 7080:4444 + links: + - postgresql + depends_on: + - postgresql + restart: on-failure + environment: + LOG_LEAK_SENSITIVE_VALUES: "true" + HYDRA_URLS_SELF_ISSUER: http://127.0.0.1:7080 + HYDRA_URLS_CONSENT: http://127.0.0.1:7081/consent + HYDRA_URLS_LOGIN: http://127.0.0.1:7081/login + HYDRA_URLS_LOGOUT: http://127.0.0.1:7081/logout + HYDRA_DSN: postgres://symfony:ChangeMe@postgresql:5432/hydra + #HYDRA_WAIT4X_DATABASE_TYPE: postgres + #HYDRA_WAIT4X_DATABASE_DSN: postgres://symfony:ChangeMe@postgresql:5432/hydra + HYDRA_ALLOW_INSECURE: "yes" + HYDRA_LEVEL: debug + + hydra-dispatcher: + build: + context: ./containers/hydra-dispatcher + container_name: hydra-dispatcher + links: + - hydra + ports: + - 7081:80 + restart: on-failure + environment: + - APP_ENV=dev + - APP_DEBUG=yes + - HYDRA_BASE_URL=http://hydra:4444 + - HYDRA_ADMIN_BASE_URL=http://hydra:4445 + # url dispatcher + - BASE_URL=http://127.0.0.1:7081 + - COOKIE_PATH=/ + - DEFAULT_LOCALE=fr + - APP_LOCALES=fr,en + volumes: + - ./containers/hydra-dispatcher/hydra:/var/www/config/hydra:ro + + hydra-passwordless: + image: cadoles/hydra-passwordless-v1 + container_name: hydra-passwordless + environment: + - HTTP_BASE_URL=http://127.0.0.1:7082/ + - SMTP_HOST=mailer + - SMTP_PORT=1025 + - SMTP_SENDER_ADDRESS=noreply@hydra.local + - SMTP_SENDER_NAME="Authentification - Passwordless" + - HYDRA_BASE_URL=http://hydra-dispatcher + links: + - hydra + - hydra-dispatcher + - mailer + ports: + - 7082:3000 + + # Service fake smtp = optionnel + mailer: + image: schickling/mailcatcher + container_name: mailer + ports: + - 1025:1025 + - 1080:1080 + + # Service fake openldap = optionnel si nineskeletor a une synchronisation avec un annuaire + openldap: + image: osixia/openldap:1.5.0 + container_name: openldap + environment: + LDAP_LOG_LEVEL: "256" + LDAP_ORGANISATION: "nine" + LDAP_DOMAIN: "nine.fr" + LDAP_ADMIN_PASSWORD: "changeme" + LDAP_CONFIG_PASSWORD: "changeme" + LDAP_READONLY_USER: "true" + LDAP_READONLY_USER_USERNAME: "readonly" + LDAP_READONLY_USER_PASSWORD: "readonly" + LDAP_TLS: "false" + volumes: + - /var/lib/ldap + - /etc/ldap/slapd.d + - /container/service/slapd/assets/certs/ + ports: + - "389:389" + - "636:636" + + # Service tool phpldapadmin = optionnel dans le cas de la présence d'un fake openldap + phpldapadmin: + image: osixia/phpldapadmin:latest + container_name: phpldapadmin + environment: + PHPLDAPADMIN_LDAP_HOSTS: "openldap" + PHPLDAPADMIN_HTTPS: "false" + ports: + - "6080:80" + depends_on: + - openldap + + # Service tool adminer = optionnel + adminer: + image: adminer + container_name: adminer + restart: always + ports: + - 6081:8080 volumes: db-data: diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index 23faa94..4102530 100755 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -214,8 +214,10 @@ class SecurityController extends AbstractController public function loginOPENID(Request $request, AuthenticationUtils $authenticationUtils) { + $state=Uuid::uuid4(); + $request->getSession()->set("oauthState",$state); $callback=$this->generateUrl('app_loginopenidcallback', array(), UrlGeneratorInterface::ABSOLUTE_URL); - $url=$this->getParameter("oauthLoginurl")."?client_id=".$this->getParameter("oauthClientid")."&redirect_uri=".$callback."&response_type=code&state=12345678&scope=openid"; + $url=$this->getParameter("oauthLoginurl")."?client_id=".$this->getParameter("oauthClientid")."&redirect_uri=".$callback."&response_type=code&state=".$state."&scope=openid"; return $this->redirect($url); } @@ -236,15 +238,20 @@ class SecurityController extends AbstractController "client_id" => $this->getParameter("oauthClientid"), "client_secret" => $this->getParameter("oauthClientsecret"), ]; - $response=$this->apiservice->run("POST",$apiurl,$query); - - if(!$response||$response->code!="200") return $this->logout($request); - $token=$response->body->access_token; - $request->getSession()->set("oauthToken",$token); + $response=$this->apiservice->run("POST",$apiurl,$query,null,"form"); + + if(!$response||$response->code!="200") die("pb openid 01"); + $accesstoken=$response->body->access_token; + $accesstokentype=$response->body->token_type; + $îdtoken=$response->body->id_token; + + $request->getSession()->set("oauthAccesstoken",$accesstoken); + $request->getSession()->set("oauthIdtoken",$îdtoken); $apiurl = $this->getParameter("oauthUserinfo"); - $response=$this->apiservice->run("GET",$apiurl,null,["Authorization"=>"token ".$token]); - if(!$response||$response->code!="200") return $this->logout($request); + + $response=$this->apiservice->run("GET",$apiurl,null,["Authorization"=>$accesstokentype." ".$accesstoken]); + if(!$response||$response->code!="200") die("pb openid 02"); $attributes=json_decode(json_encode($response->body), true); @@ -346,14 +353,21 @@ class SecurityController extends AbstractController public function logoutOPENID(Request $request) { - $token=$request->getSession()->get("oauthToken"); + $accesstoken=$request->getSession()->get("oauthAccesstoken"); + $idtoken=$request->getSession()->get("oauthIdtoken"); + $state=$request->getSession()->get("oauthState"); + $this->tokenstorage->setToken(null); $request->getSession()->invalidate(); $url=$this->getParameter("oauthLogouturl"); if($url) { - $url.="?id_token_hint=$token&scope=openid&post_logout_redirect_uri=http://127.0.0.1:8000"; + $callback=($request->isSecure()?"https://":"http://").str_replace("//","/",$this->getParameter("appWeburl").$this->getParameter("appAlias").$this->generateUrl('app_home')); + $callback=substr($callback, 0, -1); + + $url.="?id_token_hint=$idtoken&scope=openid&state=$state&post_logout_redirect_uri=$callback"; return $this->redirect($url); + } else return $this->redirect($this->generateUrl("app_home")); } diff --git a/src/Service/ApiService.php b/src/Service/ApiService.php index a7d199c..5e2c97c 100644 --- a/src/Service/ApiService.php +++ b/src/Service/ApiService.php @@ -19,12 +19,25 @@ class ApiService return \Unirest\Request\Body::json($array); } - public function run($method,$url,$query,$header=null) { + public function run($method,$url,$query,$header=null,$content="json") { // Entete - $headerini = [ - 'Accept' => 'application/json', - 'Content-Type' => 'application/json', - ]; + switch($content) { + case "json": + $headerini = [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ]; + if($query) $query = \Unirest\Request\Body::json($query); + break; + + case "form": + $headerini = [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/x-www-form-urlencoded', + ]; + if($query) $query = \Unirest\Request\Body::form($query); + break; + } if($header) $header=array_merge($headerini,$header); else $header=$headerini; @@ -42,9 +55,6 @@ class ApiService \Unirest\Request::proxy($proxyHost, $proxyPort, CURLPROXY_HTTP, true); } - //if($query) $query = \Unirest\Request\Body::json($query); - if($query) $query = http_build_query($query); - $response = false; switch($method) { case "POST":