automated wordpress saml
parent
ab559dd35a
commit
be28c1ae30
|
@ -15,4 +15,5 @@ Werkzeug==1.0.1
|
|||
zope.event==4.4
|
||||
zope.interface==5.1.0
|
||||
psycopg2==2.8.6
|
||||
Flask-SocketIO==2.8.6
|
||||
Flask-SocketIO==2.8.6
|
||||
mysql-connector-python==8.0.25
|
|
@ -1,11 +1,13 @@
|
|||
from admin import app
|
||||
from .keycloak import Keycloak
|
||||
from .keycloak_client import KeycloakClient
|
||||
from .moodle import Moodle
|
||||
from .nextcloud import Nextcloud
|
||||
|
||||
import logging as log
|
||||
from pprint import pprint
|
||||
import traceback
|
||||
import traceback, os
|
||||
|
||||
from time import sleep
|
||||
|
||||
from .nextcloud_exc import *
|
||||
from .helpers import filter_roles_list, filter_roles_listofdicts
|
||||
|
@ -13,12 +15,42 @@ from .helpers import filter_roles_list, filter_roles_listofdicts
|
|||
from flask_socketio import SocketIO, emit, join_room, leave_room, \
|
||||
close_room, rooms, disconnect, send
|
||||
socketio = SocketIO(app)
|
||||
import json
|
||||
|
||||
class Admin():
|
||||
def __init__(self):
|
||||
self.keycloak=Keycloak(verify=app.config['VERIFY'])
|
||||
self.moodle=Moodle(verify=app.config['VERIFY'])
|
||||
self.nextcloud=Nextcloud(verify=app.config['VERIFY'])
|
||||
ready=False
|
||||
while not ready:
|
||||
try:
|
||||
self.keycloak=KeycloakClient(verify=app.config['VERIFY'])
|
||||
ready=True
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
log.error('Could not connect to keycloak, waiting to be online...')
|
||||
sleep(2)
|
||||
log.warning('Keycloak connected.')
|
||||
|
||||
ready=False
|
||||
while not ready:
|
||||
try:
|
||||
self.moodle=Moodle(verify=app.config['VERIFY'])
|
||||
ready=True
|
||||
except:
|
||||
log.error('Could not connect to moodle, waiting to be online...')
|
||||
sleep(2)
|
||||
log.warning('Moodle connected.')
|
||||
|
||||
ready=False
|
||||
while not ready:
|
||||
try:
|
||||
self.nextcloud=Nextcloud(verify=app.config['VERIFY'])
|
||||
ready=True
|
||||
except:
|
||||
log.error('Could not connect to nextcloud, waiting to be online...')
|
||||
sleep(2)
|
||||
log.warning('Nextcloud connected.')
|
||||
|
||||
self.default_setup()
|
||||
|
||||
self.internal={}
|
||||
self.resync_data()
|
||||
|
@ -26,6 +58,52 @@ class Admin():
|
|||
'groups':[],
|
||||
'roles':[]}
|
||||
|
||||
## This function should be moved to postup.py
|
||||
def default_setup(self):
|
||||
log.warning('Setting defaults...')
|
||||
dduser=os.environ['DDADMIN_USER']
|
||||
ddpassword=os.environ['DDADMIN_PASSWORD']
|
||||
ddmail=os.environ['DDADMIN_EMAIL']
|
||||
|
||||
try:
|
||||
log.warning('KEYCLOAK: Adding group admin and user admin to this group')
|
||||
self.keycloak.add_group('admin')
|
||||
## Add default admin user to group admin (for nextcloud, just in case we go there)
|
||||
admin_uid=self.keycloak_admin.get_user_id('admin')
|
||||
self.keycloak_admin.group_user_add(uid,gid)
|
||||
log.warning('KEYCLOAK: OK')
|
||||
except:
|
||||
log.warning('KEYCLOAK: Seems to be there already')
|
||||
|
||||
try:
|
||||
log.warning('KEYCLOAK: Adding user ddadmin and adding to group and role admin')
|
||||
## Assign group admin to this dduser for nextcloud
|
||||
uid=self.keycloak.add_user(dduser,'DD','Admin',ddmail,ddpassword,group='admin')
|
||||
## Assign role admin to this user for keycloak, moodle and wordpress
|
||||
self.keycloak.assign_realm_roles(uid,'admin')
|
||||
log.warning('KEYCLOAK: OK')
|
||||
except:
|
||||
log.warning('KEYCLOAK: Seems to be there already')
|
||||
|
||||
try:
|
||||
log.warning('NEXTCLOUD: Adding user ddadmin and adding to group admin')
|
||||
self.nextcloud.add_user(dduser,ddpassword,group='admin',email=ddmail,displayname='DD Admin')
|
||||
log.warning('NEXTCLOUD: OK')
|
||||
except ProviderItemExists:
|
||||
log.warning('NEXTCLOUD: Seems to be there already')
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
log.warning('MOODLE: Adding user ddadmin and adding to siteadmins')
|
||||
self.moodle.create_user(ddmail,dduser,ddpassword,'DD','Admin')
|
||||
uid=self.moodle.get_user_by('username',dduser)['users'][0]['id']
|
||||
self.moodle.add_user_to_siteadmin(uid)
|
||||
log.warning('MOODLE: OK')
|
||||
except:
|
||||
log.warning('MOODLE: Seems to be there already')
|
||||
|
||||
def resync_data(self):
|
||||
self.internal={'users':self._get_mix_users(),
|
||||
'groups':self._get_mix_groups(),
|
||||
|
@ -146,15 +224,15 @@ class Admin():
|
|||
return filter_roles_listofdicts(self.keycloak.get_roles())
|
||||
|
||||
def get_keycloak_groups(self):
|
||||
log.warning('Loading keycloak groups... can take a long time...')
|
||||
log.warning('Loading keycloak groups...')
|
||||
return self.keycloak.get_groups()
|
||||
|
||||
def get_moodle_groups(self):
|
||||
log.warning('Loading moodle groups... can take a long time...')
|
||||
log.warning('Loading moodle groups...')
|
||||
return self.moodle.get_cohorts()
|
||||
|
||||
def get_nextcloud_groups(self):
|
||||
log.warning('Loading nextcloud groups... can take a long time...')
|
||||
log.warning('Loading nextcloud groups...')
|
||||
return self.nextcloud.get_groups_list()
|
||||
|
||||
def get_mix_groups(self):
|
||||
|
@ -244,24 +322,24 @@ class Admin():
|
|||
|
||||
def sync_external(self):
|
||||
for u in self.external['users']:
|
||||
log.error('Creating user: '+u['username'])
|
||||
log.info('Creating user: '+u['username'])
|
||||
self.keycloak.add_user(u['username'],u['first'],u['last'],u['email'],'1Provaprovaprova',group=u['groups'][0])
|
||||
|
||||
def sync_to_moodle(self):
|
||||
for u in self.internal['users']:
|
||||
if not u['moodle']:
|
||||
log.error('Creating moodle user: '+u['username'])
|
||||
log.info('Creating moodle user: '+u['username'])
|
||||
self.moodle.create_user(u['email'],u['username'],'-1Provaprovaprova',u['first'],u['last'])
|
||||
|
||||
def sync_to_nextcloud(self):
|
||||
for u in self.internal['users']:
|
||||
if not u['nextcloud']:
|
||||
log.error('Creating nextcloud user: '+u['username'])
|
||||
log.info('Creating nextcloud user: '+u['username'])
|
||||
group=u['keycloak_groups'][0] if len(u['keycloak_groups']) else False
|
||||
try:
|
||||
self.nextcloud.add_user(u['username'],'-1Provaprovaprova',1000,group,u['email'],u['first']+' '+u['last'])
|
||||
except ProviderItemExists:
|
||||
log.error('User '+u['username']+' already exists. Skipping...')
|
||||
log.info('User '+u['username']+' already exists. Skipping...')
|
||||
continue
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
|
|
|
@ -14,7 +14,7 @@ from jinja2 import Environment, FileSystemLoader
|
|||
from keycloak import KeycloakAdmin
|
||||
from .postgres import Postgres
|
||||
|
||||
class Keycloak():
|
||||
class KeycloakClient():
|
||||
"""https://www.keycloak.org/docs-api/13.0/rest-api/index.html
|
||||
https://github.com/marcospereirampj/python-keycloak
|
||||
https://gist.github.com/kaqfa/99829941121188d7cef8271f93f52f1f
|
||||
|
@ -31,7 +31,7 @@ class Keycloak():
|
|||
self.realm=realm
|
||||
self.verify=verify
|
||||
|
||||
self.keycloak_pg=Postgres('isard-apps-postgresql','keycloak',app.config['KEYCLOAK_POSTGRES_USER'],app.config['KEYCLOAK_POSTGRES_PASSWORD'])
|
||||
self.keycloak_pg=Postgres('isard-apps-postgresql','keycloak',os.environ['KEYCLOAK_DB_USER'],os.environ['KEYCLOAK_DB_PASSWORD'])
|
||||
|
||||
def connect(self):
|
||||
self.keycloak_admin = KeycloakAdmin(server_url=self.url,
|
||||
|
@ -39,7 +39,7 @@ class Keycloak():
|
|||
password=self.password,
|
||||
realm_name=self.realm,
|
||||
verify=self.verify)
|
||||
|
||||
# from keycloak import KeycloakAdmin
|
||||
# keycloak_admin = KeycloakAdmin(server_url="http://isard-sso-keycloak:8080/auth/",username="admin",password="keycloakkeycloak",realm_name="master",verify=False)
|
||||
|
||||
######## Example create group and subgroup
|
||||
|
@ -110,7 +110,6 @@ class Keycloak():
|
|||
|
||||
def add_user(self,username,first,last,email,password,group=False):
|
||||
# Returns user id
|
||||
log.error('Creating group: '+str(group))
|
||||
self.connect()
|
||||
username=username.lower()
|
||||
try:
|
||||
|
@ -123,22 +122,21 @@ class Keycloak():
|
|||
"value":password,
|
||||
"temporary":False}]})
|
||||
except:
|
||||
uid=self.keycloak_admin.get_user_id(username)
|
||||
log.error(uid)
|
||||
log.error(traceback.format_exc())
|
||||
|
||||
if group:
|
||||
path = '/'+group if group[1:] != '/' else group
|
||||
try:
|
||||
gid=self.keycloak_admin.get_group_by_path(path=group,search_in_subgroups=False)['id']
|
||||
log.error('group created with gid: '+str(gid))
|
||||
gid=self.keycloak_admin.get_group_by_path(path=path,search_in_subgroups=False)['id']
|
||||
except:
|
||||
self.keycloak_admin.create_group({"name":group})
|
||||
gid=self.keycloak_admin.get_group_by_path(group)['id']
|
||||
log.error(gid)
|
||||
gid=self.keycloak_admin.get_group_by_path(path)['id']
|
||||
self.keycloak_admin.group_user_add(uid,gid)
|
||||
|
||||
return uid
|
||||
|
||||
def add_user_role(self,client_id,user_id,role_id,role_name):
|
||||
self.connect()
|
||||
return self.keycloak_admin.assign_client_role(client_id="client_id", user_id="user_id", role_id="role_id", role_name="test")
|
||||
return self.keycloak_admin.assign_client_role(client_id=client_id, user_id=user_id, role_id=role_id, role_name="test")
|
||||
|
||||
def delete_user(self,userid):
|
||||
self.connect()
|
||||
|
@ -196,8 +194,9 @@ class Keycloak():
|
|||
self.connect()
|
||||
return self.keycloak_admin.get_client_roles(client_id=client_id)
|
||||
|
||||
# def add_client_role(self,client_id,roleName):
|
||||
# return self.keycloak_admin.create_client_role(client_id=client_id, {'name': roleName, 'clientRole': True})
|
||||
def add_client_role(self,client_id,name,description=''):
|
||||
self.connect()
|
||||
return self.keycloak_admin.create_client_role(client_id, {'name': name, 'description':description, 'clientRole': True})
|
||||
|
||||
|
||||
## SYSTEM
|
||||
|
@ -214,6 +213,15 @@ class Keycloak():
|
|||
rsa_key = [k for k in self.keycloak_admin.get_keys()['keys'] if k['type']=='RSA'][0]
|
||||
return {'name':rsa_key['kid'],'certificate':rsa_key['certificate']}
|
||||
|
||||
## REALM
|
||||
def assign_realm_roles(self, user_id, role):
|
||||
self.connect()
|
||||
try:
|
||||
role=[r for r in self.keycloak_admin.get_realm_roles() if r['name']==role]
|
||||
except:
|
||||
return False
|
||||
return self.keycloak_admin.assign_realm_roles(user_id=user_id, client_id=None, roles=role)
|
||||
|
||||
## CLIENTS
|
||||
def delete_client(self,clientid):
|
||||
self.connect()
|
|
@ -121,4 +121,37 @@ class Moodle():
|
|||
cohorts=self.get_cohorts()
|
||||
for cohort in cohorts:
|
||||
if user_id in self.get_cohort_members(cohort['id']): user_cohorts.append(cohort)
|
||||
return user_cohorts
|
||||
return user_cohorts
|
||||
|
||||
def add_user_to_siteadmin(self,user_id):
|
||||
q = """SELECT value FROM mdl_config WHERE name='siteadmins'"""
|
||||
value=self.moodle_pg.select(q)[0][0]
|
||||
if str(user_id) not in value:
|
||||
value=value+','+str(user_id)
|
||||
q = """UPDATE mdl_config SET value = '%s' WHERE name='siteadmins'""" % (value)
|
||||
self.moodle_pg.update(q)
|
||||
log.warning('MOODLE:ADDING THE USER TO ADMINS: This needs a purge cache in moodle!')
|
||||
|
||||
# def add_role_to_user(self, user_id, role='admin', context='missing'):
|
||||
# if role=='admin':
|
||||
# role_id=1
|
||||
# else:
|
||||
# return False
|
||||
# assignments = [{'roleid': role_id, 'userid': user_id, 'contextid': 0}]
|
||||
# self.call('core_role_assign_roles', assignments=assignments)
|
||||
# userid=user_id, role_id=role_id)
|
||||
# 'contextlevel': 1,
|
||||
|
||||
# define('CONTEXT_SYSTEM', 10);
|
||||
# define('CONTEXT_USER', 30);
|
||||
# define('CONTEXT_COURSECAT', 40);
|
||||
# define('CONTEXT_COURSE', 50);
|
||||
# define('CONTEXT_MODULE', 70);
|
||||
# define('CONTEXT_BLOCK', 80);
|
||||
|
||||
# 'contextlevel': , 'instanceid'
|
||||
# $assignment = array( 'roleid' => $role_id, 'userid' => $user_id, 'contextid' => $context_id );
|
||||
# $assignments = array( $assignment );
|
||||
# $params = array( 'assignments' => $assignments );
|
||||
|
||||
# $response = call_moodle( 'core_role_assign_roles', $params, $token );
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
import time
|
||||
from admin import app
|
||||
from datetime import datetime, timedelta
|
||||
import pprint
|
||||
|
||||
import logging as log
|
||||
import traceback
|
||||
import yaml, json
|
||||
|
||||
import mysql.connector
|
||||
|
||||
class Mysql():
|
||||
|
||||
def __init__(self,host,database,user,password):
|
||||
self.conn = mysql.connector.connect(
|
||||
host=host,
|
||||
database=database,
|
||||
user=user,
|
||||
password=password)
|
||||
|
||||
def select(self,sql):
|
||||
self.cur = self.conn.cursor()
|
||||
self.cur.execute(sql)
|
||||
data=self.cur.fetchall()
|
||||
self.cur.close()
|
||||
return data
|
||||
|
||||
def update(self,sql):
|
||||
self.cur = self.conn.cursor()
|
||||
self.cur.execute(sql)
|
||||
self.conn.commit()
|
||||
self.cur.close()
|
|
@ -126,11 +126,14 @@ class Nextcloud():
|
|||
# log.error(traceback.format_exc())
|
||||
# raise
|
||||
|
||||
def add_user(self,userid,userpassword,quota,group=False,email='',displayname=''):
|
||||
if group:
|
||||
data={'userid':userid,'password':userpassword,'quota':quota,'groups[]':group,'email':email,'displayname':displayname}
|
||||
else:
|
||||
data={'userid':userid,'password':userpassword,'quota':quota,'email':email,'displayname':displayname}
|
||||
def add_user(self,userid,userpassword,quota=False,group=False,email='',displayname=''):
|
||||
data={'userid':userid,'password':userpassword,'quota':quota,'groups[]':group,'email':email,'displayname':displayname}
|
||||
if not group: del data['group']
|
||||
if not quota: del data['quota']
|
||||
# if group:
|
||||
# data={'userid':userid,'password':userpassword,'quota':quota,'groups[]':group,'email':email,'displayname':displayname}
|
||||
# else:
|
||||
# data={'userid':userid,'password':userpassword,'quota':quota,'email':email,'displayname':displayname}
|
||||
url = self.apiurl + "users?format=json"
|
||||
headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
|
|
|
@ -82,6 +82,7 @@ class Postup():
|
|||
(3, 'core_cohort_delete_cohorts'),
|
||||
(3, 'core_cohort_search_cohorts'),
|
||||
(3, 'core_cohort_update_cohorts'),
|
||||
(3, 'core_role_assign_roles'),
|
||||
(3, 'core_cohort_get_cohorts');""")
|
||||
|
||||
self.pg.update("""INSERT INTO "mdl_external_services_users" ("externalserviceid", "userid", "iprestriction", "validuntil", "timecreated") VALUES
|
||||
|
|
|
@ -11,7 +11,7 @@ import yaml, json
|
|||
import psycopg2
|
||||
|
||||
from admin.lib.postgres import Postgres
|
||||
from admin.lib.keycloak import Keycloak
|
||||
from admin.lib.keycloak_client import KeycloakClient
|
||||
|
||||
import string, random
|
||||
|
||||
|
@ -102,6 +102,8 @@ class MoodleSaml():
|
|||
except:
|
||||
print('Error adding saml on keycloak')
|
||||
|
||||
self.add_client_roles()
|
||||
|
||||
def activate_saml_plugin(self):
|
||||
## After you need to purge moodle caches: /var/www/html # php admin/cli/purge_caches.php
|
||||
return self.pg.update("""UPDATE "mdl_config" SET value = 'email,saml2' WHERE "name" = 'auth'""")
|
||||
|
@ -110,18 +112,18 @@ class MoodleSaml():
|
|||
return self.pg.select("""SELECT * FROM "mdl_config" WHERE "name" = 'siteidentifier'""")[0][2]
|
||||
|
||||
def parse_idp_metadata(self):
|
||||
keycloak=Keycloak()
|
||||
keycloak=KeycloakClient()
|
||||
rsa=keycloak.get_server_rsa_key()
|
||||
keycloak=None
|
||||
return '<md:EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Name="urn:keycloak"><md:EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://sso.'+os.environ['DOMAIN']+'/auth/realms/master"><md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><md:KeyDescriptor use="signing"><ds:KeyInfo><ds:KeyName>'+rsa['name']+'</ds:KeyName><ds:X509Data><ds:X509Certificate>'+rsa['certificate']+'</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.'+os.environ['DOMAIN']+'/auth/realms/master/protocol/saml/resolve" index="0"/><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.'+os.environ['DOMAIN']+'/auth/realms/master/protocol/saml"/><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.'+os.environ['DOMAIN']+'/auth/realms/master/protocol/saml"/><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.'+os.environ['DOMAIN']+'/auth/realms/master/protocol/saml"/><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.'+os.environ['DOMAIN']+'/auth/realms/master/protocol/saml"/><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.'+os.environ['DOMAIN']+'/auth/realms/master/protocol/saml"/><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.'+os.environ['DOMAIN']+'/auth/realms/master/protocol/saml"/><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.'+os.environ['DOMAIN']+'/auth/realms/master/protocol/saml"/></md:IDPSSODescriptor></md:EntityDescriptor></md:EntitiesDescriptor>'
|
||||
|
||||
def set_keycloak_moodle_saml_plugin(self):
|
||||
keycloak=Keycloak()
|
||||
keycloak=KeycloakClient()
|
||||
keycloak.add_moodle_client()
|
||||
keycloak=None
|
||||
|
||||
def delete_keycloak_moodle_saml_plugin(self):
|
||||
keycloak=Keycloak()
|
||||
keycloak=KeycloakClient()
|
||||
keycloak.delete_client('a92d5417-92b6-4678-9cb9-51bc0edcee8c')
|
||||
keycloak=None
|
||||
|
||||
|
@ -240,9 +242,16 @@ class MoodleSaml():
|
|||
"manage" : True
|
||||
}
|
||||
}
|
||||
keycloak=Keycloak()
|
||||
keycloak=KeycloakClient()
|
||||
keycloak.add_client(client)
|
||||
keycloak=None
|
||||
|
||||
def add_client_roles(self):
|
||||
keycloak=KeycloakClient()
|
||||
keycloak.add_client_role('a92d5417-92b6-4678-9cb9-51bc0edcee8c','admin','Moodle admins')
|
||||
keycloak.add_client_role('a92d5417-92b6-4678-9cb9-51bc0edcee8c','manager','Moodle managers')
|
||||
keycloak.add_client_role('a92d5417-92b6-4678-9cb9-51bc0edcee8c','teacher','Moodle teachers')
|
||||
keycloak.add_client_role('a92d5417-92b6-4678-9cb9-51bc0edcee8c','student','Moodle students')
|
||||
keycloak=None
|
||||
|
||||
m=MoodleSaml()
|
||||
|
|
|
@ -11,7 +11,7 @@ import yaml, json
|
|||
import psycopg2
|
||||
|
||||
from admin.lib.postgres import Postgres
|
||||
from admin.lib.keycloak import Keycloak
|
||||
from admin.lib.keycloak_client import KeycloakClient
|
||||
|
||||
import string, random
|
||||
|
||||
|
@ -20,6 +20,12 @@ app['config']={}
|
|||
|
||||
class NextcloudSaml():
|
||||
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:
|
||||
|
@ -81,6 +87,7 @@ class NextcloudSaml():
|
|||
try:
|
||||
self.set_nextcloud_saml_plugin()
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
print('Error adding saml on nextcloud')
|
||||
|
||||
try:
|
||||
|
@ -88,6 +95,13 @@ class NextcloudSaml():
|
|||
except:
|
||||
print('Error adding saml on keycloak')
|
||||
|
||||
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.pg.update("""UPDATE "mdl_config" SET value = 'email,saml2' WHERE "name" = 'auth'""")
|
||||
|
@ -96,20 +110,20 @@ class NextcloudSaml():
|
|||
# return self.pg.select("""SELECT * FROM "mdl_config" WHERE "name" = 'siteidentifier'""")[0][2]
|
||||
|
||||
def parse_idp_cert(self):
|
||||
keycloak=Keycloak()
|
||||
rsa=keycloak.get_server_rsa_key()
|
||||
keycloak=None
|
||||
self.connect()
|
||||
rsa=self.keycloak.get_server_rsa_key()
|
||||
self.keycloak=None
|
||||
return rsa['certificate']
|
||||
|
||||
def set_keycloak_nextcloud_saml_plugin(self):
|
||||
keycloak=Keycloak()
|
||||
keycloak.add_nextcloud_client()
|
||||
keycloak=None
|
||||
self.connect()
|
||||
self.keycloak.add_nextcloud_client()
|
||||
self.keycloak=None
|
||||
|
||||
def delete_keycloak_nextcloud_saml_plugin(self):
|
||||
keycloak=Keycloak()
|
||||
keycloak.delete_client('bef873f0-2079-4876-8657-067de27d01b7')
|
||||
keycloak=None
|
||||
self.connect()
|
||||
self.keycloak.delete_client('bef873f0-2079-4876-8657-067de27d01b7')
|
||||
self.keycloak=None
|
||||
|
||||
def set_nextcloud_saml_plugin(self):
|
||||
self.pg.update("""INSERT INTO "oc_appconfig" ("appid", "configkey", "configvalue") VALUES
|
||||
|
@ -235,10 +249,8 @@ class NextcloudSaml():
|
|||
"manage" : True
|
||||
}
|
||||
}
|
||||
keycloak=Keycloak()
|
||||
keycloak.add_client(client)
|
||||
keycloak=None
|
||||
|
||||
|
||||
self.connect()
|
||||
self.keycloak.add_client(client)
|
||||
self.keycloak=None
|
||||
|
||||
n=NextcloudSaml()
|
|
@ -0,0 +1,295 @@
|
|||
#!/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')
|
||||
|
||||
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','Moodle admins')
|
||||
self.keycloak.add_client_role('630601f8-25d1-4822-8741-c93affd2cd84','manager','Moodle managers')
|
||||
self.keycloak.add_client_role('630601f8-25d1-4822-8741-c93affd2cd84','teacher','Moodle teachers')
|
||||
self.keycloak.add_client_role('630601f8-25d1-4822-8741-c93affd2cd84','student','Moodle students')
|
||||
self.keycloak=None
|
||||
|
||||
nw=WordpressSaml()
|
|
@ -28,6 +28,8 @@ services:
|
|||
- KEYCLOAK_PASSWORD=${KEYCLOAK_PASSWORD}
|
||||
- PROXY_ADDRESS_FORWARDING=true
|
||||
- KEYCLOAK_FRONTEND_URL=https://sso.${DOMAIN}/auth/
|
||||
- DDADMIN_USER=${DDADMIN_USER}
|
||||
- DDADMIN_PASSWORD=${DDADMIN_PASSWORD}
|
||||
#- KEYCLOAK_LOGLEVEL=ALL
|
||||
#- Dkeycloak.profile.feature.upload_scripts=enabled
|
||||
depends_on:
|
||||
|
|
|
@ -15,3 +15,6 @@
|
|||
# #curl https://moodle.isardvdi.site/auth/saml2/sp/metadata.php
|
||||
|
||||
# # Import as client provider
|
||||
|
||||
|
||||
# get-roles --cclientid test-client --rolename operations
|
||||
|
|
Loading…
Reference in New Issue