[correu] Add registration for SAML client
parent
7bcc08bc6d
commit
4324812807
8
dd-ctl
8
dd-ctl
|
@ -476,11 +476,9 @@ saml_certificates(){
|
||||||
echo " --> Setting up SAML for wordpress"
|
echo " --> Setting up SAML for wordpress"
|
||||||
docker exec -ti dd-sso-admin sh -c "export PYTHONWARNINGS='ignore:Unverified HTTPS request' && cd /admin/saml_scripts/ && python3 wordpress_saml.py"
|
docker exec -ti dd-sso-admin sh -c "export PYTHONWARNINGS='ignore:Unverified HTTPS request' && cd /admin/saml_scripts/ && python3 wordpress_saml.py"
|
||||||
|
|
||||||
# SAML PLUGIN MOODLE
|
# SAML PLUGIN EMAIL
|
||||||
# echo "To add SAML to moodle:"
|
echo " --> Setting up SAML for email"
|
||||||
# echo "1.-Activate SAML plugin in moodle extensions, regenerate certificate, lock certificate"
|
docker exec -ti dd-sso-admin sh -c "export PYTHONWARNINGS='ignore:Unverified HTTPS request' && cd /admin/saml_scripts/ && python3 email_saml.py"
|
||||||
# echo "2.-Then run: docker exec -ti dd-sso-admin python3 /admin/nextcloud_saml.py"
|
|
||||||
# echo "3.-"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_for_moodle(){
|
wait_for_moodle(){
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
#
|
||||||
|
# Copyright © 2022 Evilham
|
||||||
|
#
|
||||||
|
# This file is part of DD
|
||||||
|
#
|
||||||
|
# DD is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
# option) any later version.
|
||||||
|
#
|
||||||
|
# DD is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with DD. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
import logging as log
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
from lib.keycloak_client import KeycloakClient
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Python 3.9+
|
||||||
|
from functools import cache # type: ignore # Currently targetting 3.8
|
||||||
|
except ImportError:
|
||||||
|
# Up to python 3.8
|
||||||
|
from functools import cached_property as cache
|
||||||
|
|
||||||
|
|
||||||
|
app: Dict[str, Dict[str, str]] = {}
|
||||||
|
app["config"] = {}
|
||||||
|
|
||||||
|
|
||||||
|
class SamlService(object):
|
||||||
|
"""
|
||||||
|
Generic class to manage a SAML service on keycloak
|
||||||
|
"""
|
||||||
|
|
||||||
|
keycloak: KeycloakClient
|
||||||
|
domain: str = os.environ["DOMAIN"]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.keycloak = KeycloakClient()
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def public_cert(self) -> str:
|
||||||
|
"""
|
||||||
|
Read the public SAML certificate as used by keycloak
|
||||||
|
"""
|
||||||
|
ready = False
|
||||||
|
basepath = os.path.dirname(__file__)
|
||||||
|
while not ready:
|
||||||
|
# TODO: Check why they were using a loop
|
||||||
|
try:
|
||||||
|
with open(
|
||||||
|
os.path.abspath(
|
||||||
|
os.path.join(basepath, "../saml_certs/public.cert")
|
||||||
|
),
|
||||||
|
"r",
|
||||||
|
) as crt:
|
||||||
|
app["config"]["PUBLIC_CERT"] = crt.read()
|
||||||
|
ready = True
|
||||||
|
except IOError:
|
||||||
|
log.warning("Could not get public certificate for SAML. Retrying...")
|
||||||
|
log.warning(
|
||||||
|
" You should generate them: /admin/saml_certs # openssl req -nodes -new -x509 -keyout private.key -out public.cert"
|
||||||
|
)
|
||||||
|
time.sleep(2)
|
||||||
|
except:
|
||||||
|
log.error(traceback.format_exc())
|
||||||
|
log.info("Got public SAML certificate")
|
||||||
|
return app["config"]["PUBLIC_CERT"]
|
||||||
|
|
||||||
|
def get_client(self, client_id: str) -> Any:
|
||||||
|
# TODO: merge with keycloak_config.py
|
||||||
|
self.keycloak.connect()
|
||||||
|
k = self.keycloak.keycloak_admin
|
||||||
|
|
||||||
|
clients = k.get_clients()
|
||||||
|
client = next(filter(lambda c: c["clientId"] == client_id, clients), None)
|
||||||
|
return (k, client)
|
||||||
|
|
||||||
|
def set_client(self, client_id: str, client_overrides: Dict[str, Any]) -> str:
|
||||||
|
(k, client) = self.get_client(client_id)
|
||||||
|
if client is None:
|
||||||
|
client_uid = k.create_client(client_overrides)
|
||||||
|
else:
|
||||||
|
client_uid = client["id"]
|
||||||
|
k.update_client(client_uid, client_overrides)
|
||||||
|
return client_id
|
||||||
|
|
||||||
|
def configure(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EmailSaml(SamlService):
|
||||||
|
client_name: str = "correu"
|
||||||
|
client_description: str = "Client for the DD-managed email service"
|
||||||
|
email_domain: str
|
||||||
|
|
||||||
|
def __init__(self, email_domain: str, enabled: bool = False):
|
||||||
|
super(EmailSaml, self).__init__()
|
||||||
|
self.email_domain = email_domain
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enabled(self) -> bool:
|
||||||
|
return bool(self.email_domain)
|
||||||
|
|
||||||
|
def configure(self) -> None:
|
||||||
|
srv_base = f"https://correu.{self.domain}"
|
||||||
|
client_id = f"{srv_base}/metadata/"
|
||||||
|
client_overrides: Dict[str, Any] = {
|
||||||
|
"name": self.client_name,
|
||||||
|
"description": self.client_description,
|
||||||
|
"clientId": client_id,
|
||||||
|
"baseUrl": f"{srv_base}/login",
|
||||||
|
"enabled": self.enabled,
|
||||||
|
"redirectUris": [
|
||||||
|
f"{srv_base}/*",
|
||||||
|
],
|
||||||
|
"webOrigins": [srv_base],
|
||||||
|
"consentRequired": False,
|
||||||
|
"protocol": "saml",
|
||||||
|
"attributes": {
|
||||||
|
"saml.assertion.signature": True,
|
||||||
|
"saml_idp_initiated_sso_relay_state": f"{srv_base}/login",
|
||||||
|
"saml_assertion_consumer_url_redirect": f"{srv_base}/acs",
|
||||||
|
"saml.force.post.binding": True,
|
||||||
|
"saml.multivalued.roles": False,
|
||||||
|
"saml.encrypt": False,
|
||||||
|
"saml_assertion_consumer_url_post": f"{srv_base}/acs",
|
||||||
|
"saml.server.signature": True,
|
||||||
|
"saml_idp_initiated_sso_url_name": f"{srv_base}/acs",
|
||||||
|
"saml.server.signature.keyinfo.ext": False,
|
||||||
|
"exclude.session.state.from.auth.response": False,
|
||||||
|
"saml_single_logout_service_url_redirect": f"{srv_base}/ls",
|
||||||
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
|
"saml_force_name_id_format": False,
|
||||||
|
"saml.client.signature": False,
|
||||||
|
"tls.client.certificate.bound.access.tokens": False,
|
||||||
|
"saml.authnstatement": True,
|
||||||
|
"display.on.consent.screen": False,
|
||||||
|
"saml_name_id_format": "username",
|
||||||
|
"saml_signature_canonicalization_method": "http://www.w3.org/2001/10/xml-exc-c14n#",
|
||||||
|
"saml.onetimeuse.condition": False,
|
||||||
|
},
|
||||||
|
"protocolMappers": [
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"protocol": "saml",
|
||||||
|
"protocolMapper": "saml-user-property-mapper",
|
||||||
|
"consentRequired": False,
|
||||||
|
"config": {
|
||||||
|
"attribute.nameformat": "Basic",
|
||||||
|
"user.attribute": "username",
|
||||||
|
"friendly.name": "username",
|
||||||
|
"attribute.name": "username",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "email",
|
||||||
|
"protocol": "saml",
|
||||||
|
"protocolMapper": "saml-user-property-mapper",
|
||||||
|
"consentRequired": False,
|
||||||
|
"config": {
|
||||||
|
"attribute.nameformat": "Basic",
|
||||||
|
"user.attribute": "email",
|
||||||
|
"friendly.name": "email",
|
||||||
|
"attribute.name": "email",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
self.set_client(client_id, client_overrides)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
email_domain = os.environ.get("MANAGED_EMAIL_DOMAIN", "")
|
||||||
|
log.info("Configuring SAML client for Email")
|
||||||
|
EmailSaml(email_domain).configure()
|
|
@ -48,3 +48,4 @@ services:
|
||||||
environment:
|
environment:
|
||||||
- VERIFY="false" # In development do not verify certificates
|
- VERIFY="false" # In development do not verify certificates
|
||||||
- DOMAIN=${DOMAIN}
|
- DOMAIN=${DOMAIN}
|
||||||
|
- MANAGED_EMAIL_DOMAIN=${MANAGED_EMAIL_DOMAIN}
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
TITLE="DD"
|
TITLE="DD"
|
||||||
TITLE_SHORT="DD"
|
TITLE_SHORT="DD"
|
||||||
DOMAIN=mydomain.com
|
DOMAIN=mydomain.com
|
||||||
|
# If defined, DD will be managing email for this domain
|
||||||
|
#MANAGED_EMAIL_DOMAIN=${DOMAIN}
|
||||||
LETSENCRYPT_DNS=
|
LETSENCRYPT_DNS=
|
||||||
LETSENCRYPT_EMAIL=
|
LETSENCRYPT_EMAIL=
|
||||||
# Generate letsencrypt certificate for root domain
|
# Generate letsencrypt certificate for root domain
|
||||||
|
|
Loading…
Reference in New Issue