[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"
|
||||
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
|
||||
# echo "To add SAML to moodle:"
|
||||
# echo "1.-Activate SAML plugin in moodle extensions, regenerate certificate, lock certificate"
|
||||
# echo "2.-Then run: docker exec -ti dd-sso-admin python3 /admin/nextcloud_saml.py"
|
||||
# echo "3.-"
|
||||
# SAML PLUGIN EMAIL
|
||||
echo " --> Setting up SAML for email"
|
||||
docker exec -ti dd-sso-admin sh -c "export PYTHONWARNINGS='ignore:Unverified HTTPS request' && cd /admin/saml_scripts/ && python3 email_saml.py"
|
||||
}
|
||||
|
||||
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:
|
||||
- VERIFY="false" # In development do not verify certificates
|
||||
- DOMAIN=${DOMAIN}
|
||||
- MANAGED_EMAIL_DOMAIN=${MANAGED_EMAIL_DOMAIN}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
TITLE="DD"
|
||||
TITLE_SHORT="DD"
|
||||
DOMAIN=mydomain.com
|
||||
# If defined, DD will be managing email for this domain
|
||||
#MANAGED_EMAIL_DOMAIN=${DOMAIN}
|
||||
LETSENCRYPT_DNS=
|
||||
LETSENCRYPT_EMAIL=
|
||||
# Generate letsencrypt certificate for root domain
|
||||
|
|
Loading…
Reference in New Issue