digitaldemocratic/admin/src/wordpress_saml.py

296 lines
14 KiB
Python

#!/usr/bin/env python
# coding=utf-8
import time, os
from datetime import datetime, timedelta
import pprint
import logging as log
import traceback
import yaml, json
import psycopg2
from admin.lib.mysql import Mysql
from admin.lib.keycloak_client import KeycloakClient
import string, random
app={}
app['config']={}
class WordpressSaml():
def __init__(self):
self.url="http://isard-sso-keycloak:8080/auth/"
self.username=os.environ['KEYCLOAK_USER']
self.password=os.environ['KEYCLOAK_PASSWORD']
self.realm='master'
self.verify=True
ready=False
while not ready:
try:
self.db=Mysql('isard-apps-mariadb','wordpress','root',os.environ['MARIADB_PASSWORD'])
ready=True
except:
log.warning('Could not connect to wordpress database. Retrying...')
time.sleep(2)
log.info('Connected to wordpress database.')
ready=False
while not ready:
try:
with open(os.path.join("./saml_certs/public.cert"),"r") as crt:
app['config']['PUBLIC_CERT_RAW']=crt.read()
app['config']['PUBLIC_CERT']=self.cert_prepare(app['config']['PUBLIC_CERT_RAW'])
ready=True
except IOError:
log.warning('Could not get public certificate to be used in wordpress. 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 moodle srt certificate to be used in wordpress.')
ready=False
while not ready:
try:
with open(os.path.join("./saml_certs/private.key"),"r") as pem:
app['config']['PRIVATE_KEY']=self.cert_prepare(pem.read())
ready=True
except IOError:
log.warning('Could not get private key to be used in wordpress. Retrying...')
log.warning(' You should generate them: /admin/saml_certs # openssl req -nodes -new -x509 -keyout private.key -out public.cert')
time.sleep(2)
log.info('Got moodle pem certificate to be used in wordpress.')
# ## This seems related to the fact that the certificate generated the first time does'nt work.
# ## And when regenerating the certificate de privatekeypass seems not to be used and instead it
# ## will use always this code as filename: 0f635d0e0f3874fff8b581c132e6c7a7
# ## As this bug I'm not able to solve, the process is:
# ## 1.- Bring up moodle and regenerate certificates on saml2 plugin in plugins-authentication
# ## 2.- Execute this script
# ## 3.- Cleanup all caches in moodle (Development tab)
# # with open(os.path.join("./moodledata/saml2/"+os.environ['NEXTCLOUD_SAML_PRIVATEKEYPASS'].replace("moodle."+os.environ['DOMAIN'],'')+'.idp.xml'),"w") as xml:
# # xml.write(self.parse_idp_metadata())
# with open(os.path.join("./moodledata/saml2/0f635d0e0f3874fff8b581c132e6c7a7.idp.xml"),"w") as xml:
# xml.write(self.parse_idp_metadata())
try:
self.reset_saml()
except:
print(traceback.format_exc())
print('Error resetting saml on wordpress')
try:
self.delete_keycloak_wordpress_saml_plugin()
except:
print('Error resetting saml on keycloak')
try:
self.set_wordpress_saml_plugin()
except:
print(traceback.format_exc())
print('Error adding saml on wordpress')
try:
self.add_keycloak_wordpress_saml()
except:
print('Error adding saml on keycloak')
# SAML clients don't work well with composite roles so disabling and adding on realm
# self.add_client_roles()
def connect(self):
self.keycloak= KeycloakClient(url=self.url,
username=self.username,
password=self.password,
realm=self.realm,
verify=self.verify)
# def activate_saml_plugin(self):
# ## After you need to purge moodle caches: /var/www/html # php admin/cli/purge_caches.php
# return self.db.update("""UPDATE "mdl_config" SET value = 'email,saml2' WHERE "name" = 'auth'""")
# def get_privatekey_pass(self):
# return self.db.select("""SELECT * FROM "mdl_config" WHERE "name" = 'siteidentifier'""")[0][2]
def cert_prepare(self,cert):
return ''.join(cert.split('-----')[2].splitlines())
def parse_idp_cert(self):
self.connect()
rsa=self.keycloak.get_server_rsa_key()
self.keycloak=None
return rsa['certificate']
def set_keycloak_wordpress_saml_plugin(self):
self.connect()
self.keycloak.add_wordpress_client()
self.keycloak=None
def delete_keycloak_wordpress_saml_plugin(self):
self.connect()
self.keycloak.delete_client('630601f8-25d1-4822-8741-c93affd2cd84')
self.keycloak=None
def set_wordpress_saml_plugin(self):
# ('active_plugins', 'a:2:{i:0;s:33:\"edwiser-bridge/edwiser-bridge.php\";i:1;s:17:\"onelogin_saml.php\";}', 'yes'),
self.db.update("""INSERT INTO wp_options (option_name, option_value, autoload) VALUES
('onelogin_saml_enabled', 'on', 'yes'),
('onelogin_saml_idp_entityid', 'Saml Login', 'yes'),
('onelogin_saml_idp_sso', 'https://sso.%s/auth/realms/master/protocol/saml', 'yes'),
('onelogin_saml_idp_slo', 'https://sso.%s/auth/realms/master/protocol/saml', 'yes'),
('onelogin_saml_idp_x509cert', '%s', 'yes'),
('onelogin_saml_autocreate', 'on', 'yes'),
('onelogin_saml_updateuser', 'on', 'yes'),
('onelogin_saml_forcelogin', 'on', 'yes'),
('onelogin_saml_slo', 'on', 'yes'),
('onelogin_saml_keep_local_login', '', 'yes'),
('onelogin_saml_alternative_acs', '', 'yes'),
('onelogin_saml_account_matcher', 'username', 'yes'),
('onelogin_saml_trigger_login_hook', '', 'yes'),
('onelogin_saml_multirole', '', 'yes'),
('onelogin_saml_trusted_url_domains', '', 'yes'),
('onelogin_saml_attr_mapping_username', 'username', 'yes'),
('onelogin_saml_attr_mapping_mail', 'email', 'yes'),
('onelogin_saml_attr_mapping_firstname', 'givenName', 'yes'),
('onelogin_saml_attr_mapping_lastname', 'sn', 'yes'),
('onelogin_saml_attr_mapping_nickname', '', 'yes'),
('onelogin_saml_attr_mapping_role', 'Role', 'yes'),
('onelogin_saml_attr_mapping_rememberme', '', 'yes'),
('onelogin_saml_role_mapping_administrator', 'admin', 'yes'),
('onelogin_saml_role_mapping_editor', 'manager', 'yes'),
('onelogin_saml_role_mapping_author', 'coursecreator', 'yes'),
('onelogin_saml_role_mapping_contributor', 'teacher', 'yes'),
('onelogin_saml_role_mapping_subscriber', '', 'yes'),
('onelogin_saml_role_mapping_multivalued_in_one_attribute_value', 'on', 'yes'),
('onelogin_saml_role_mapping_multivalued_pattern', '', 'yes'),
('onelogin_saml_role_order_administrator', '', 'yes'),
('onelogin_saml_role_order_editor', '', 'yes'),
('onelogin_saml_role_order_author', '', 'yes'),
('onelogin_saml_role_order_contributor', '', 'yes'),
('onelogin_saml_role_order_subscriber', '', 'yes'),
('onelogin_saml_customize_action_prevent_local_login', '', 'yes'),
('onelogin_saml_customize_action_prevent_reset_password', '', 'yes'),
('onelogin_saml_customize_action_prevent_change_password', '', 'yes'),
('onelogin_saml_customize_action_prevent_change_mail', '', 'yes'),
('onelogin_saml_customize_stay_in_wordpress_after_slo', 'on', 'yes'),
('onelogin_saml_customize_links_user_registration', '', 'yes'),
('onelogin_saml_customize_links_lost_password', '', 'yes'),
('onelogin_saml_customize_links_saml_login', '', 'yes'),
('onelogin_saml_advanced_settings_debug', '', 'yes'),
('onelogin_saml_advanced_settings_strict_mode', '', 'yes'),
('onelogin_saml_advanced_settings_sp_entity_id', '', 'yes'),
('onelogin_saml_advanced_idp_lowercase_url_encoding', '', 'yes'),
('onelogin_saml_advanced_settings_nameid_encrypted', '', 'yes'),
('onelogin_saml_advanced_settings_authn_request_signed', 'on', 'yes'),
('onelogin_saml_advanced_settings_logout_request_signed', 'on', 'yes'),
('onelogin_saml_advanced_settings_logout_response_signed', 'on', 'yes'),
('onelogin_saml_advanced_settings_want_message_signed', '', 'yes'),
('onelogin_saml_advanced_settings_want_assertion_signed', '', 'yes'),
('onelogin_saml_advanced_settings_want_assertion_encrypted', '', 'yes'),
('onelogin_saml_advanced_settings_retrieve_parameters_from_server', '', 'yes'),
('onelogin_saml_advanced_nameidformat', 'unspecified', 'yes'),
('onelogin_saml_advanced_requestedauthncontext', '', 'yes'),
('onelogin_saml_advanced_settings_sp_x509cert', '%s', 'yes'),
('onelogin_saml_advanced_settings_sp_privatekey', '%s', 'yes'),
('onelogin_saml_advanced_signaturealgorithm', 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', 'yes'),
('onelogin_saml_advanced_digestalgorithm', 'http://www.w3.org/2000/09/xmldsig#sha1', 'yes');""" % (os.environ['DOMAIN'],os.environ['DOMAIN'],self.parse_idp_cert(),app['config']['PUBLIC_CERT'],app['config']['PRIVATE_KEY']))
def reset_saml(self):
self.db.update("""DELETE FROM wp_options WHERE option_name LIKE 'onelogin_saml_%'""")
def add_keycloak_wordpress_saml(self):
client={"id" : "630601f8-25d1-4822-8741-c93affd2cd84",
"clientId" : "php-saml",
"surrogateAuthRequired" : False,
"enabled" : True,
"alwaysDisplayInConsole" : False,
"clientAuthenticatorType" : "client-secret",
"redirectUris" : [ "https://wp."+os.environ['DOMAIN']+"/wp-login.php?saml_acs" ],
"webOrigins" : [ "https://wp."+os.environ['DOMAIN'] ],
"notBefore" : 0,
"bearerOnly" : False,
"consentRequired" : False,
"standardFlowEnabled" : True,
"implicitFlowEnabled" : False,
"directAccessGrantsEnabled" : False,
"serviceAccountsEnabled" : False,
"publicClient" : False,
"frontchannelLogout" : True,
"protocol" : "saml",
"attributes" : {
"saml.force.post.binding" : True,
"saml_assertion_consumer_url_post" : "https://wp."+os.environ['DOMAIN']+"/wp-login.php?saml_acs",
"saml.server.signature" : True,
"saml.server.signature.keyinfo.ext" : False,
"saml.signing.certificate" : app['config']['PUBLIC_CERT_RAW'],
"saml_single_logout_service_url_redirect" : "https://wp."+os.environ['DOMAIN']+"/wp-login.php?saml_sls",
"saml.signature.algorithm" : "RSA_SHA256",
"saml_force_name_id_format" : False,
"saml.client.signature" : True,
"saml.authnstatement" : True,
"saml_name_id_format" : "username",
"saml_signature_canonicalization_method" : "http://www.w3.org/2001/10/xml-exc-c14n#"
},
"authenticationFlowBindingOverrides" : { },
"fullScopeAllowed" : True,
"nodeReRegistrationTimeout" : -1,
"protocolMappers" : [ {
"id" : "72c6175e-bd07-4c27-abd6-4e4ae38d834b",
"name" : "username",
"protocol" : "saml",
"protocolMapper" : "saml-user-attribute-mapper",
"consentRequired" : False,
"config" : {
"attribute.nameformat" : "Basic",
"user.attribute" : "username",
"friendly.name" : "username",
"attribute.name" : "username"
}
}, {
"id" : "abd6562f-4732-4da9-987f-b1a6ad6605fa",
"name" : "roles",
"protocol" : "saml",
"protocolMapper" : "saml-role-list-mapper",
"consentRequired" : False,
"config" : {
"single" : True,
"attribute.nameformat" : "Basic",
"friendly.name" : "Roles",
"attribute.name" : "Role"
}
}, {
"id" : "50aafb71-d91c-4bc7-bb60-e1ae0222aab3",
"name" : "email",
"protocol" : "saml",
"protocolMapper" : "saml-user-property-mapper",
"consentRequired" : False,
"config" : {
"attribute.nameformat" : "Basic",
"user.attribute" : "email",
"friendly.name" : "email",
"attribute.name" : "email"
}
} ],
"defaultClientScopes" : [ "web-origins", "role_list", "roles", "profile", "email" ],
"optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ],
"access" : {
"view" : True,
"configure" : True,
"manage" : True
}
}
self.connect()
self.keycloak.add_client(client)
self.keycloak=None
def add_client_roles(self):
self.connect()
self.keycloak.add_client_role('630601f8-25d1-4822-8741-c93affd2cd84','admin','Wordpress admins')
self.keycloak.add_client_role('630601f8-25d1-4822-8741-c93affd2cd84','manager','Wordpress managers')
self.keycloak.add_client_role('630601f8-25d1-4822-8741-c93affd2cd84','teacher','Wordpress teachers')
self.keycloak.add_client_role('630601f8-25d1-4822-8741-c93affd2cd84','student','Wordpress students')
self.keycloak=None
nw=WordpressSaml()