From 7f786e6a0fb257d177df94cd916ceb8c192a6b23 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 27 May 2021 00:44:18 +0200 Subject: [PATCH] progress --- admin/src/admin/lib/admin.py | 73 +++++++++++++++---- admin/src/admin/lib/keycloak.py | 70 +++++++++++++++--- admin/src/admin/lib/moodle.py | 35 +++++++-- admin/src/admin/lib/nextcloud.py | 32 +++++++- admin/src/admin/lib/postgres.py | 6 ++ admin/src/admin/static/js/external.js | 18 +++++ admin/src/admin/static/js/users.js | 36 +++++++++ .../static/templates/pages/external.html | 1 + .../admin/static/templates/pages/users.html | 2 + admin/src/admin/views/MenuViews.py | 16 +++- admin/src/moodle_saml.py | 27 +++---- 11 files changed, 263 insertions(+), 53 deletions(-) diff --git a/admin/src/admin/lib/admin.py b/admin/src/admin/lib/admin.py index 9eda8a2..ed80c45 100644 --- a/admin/src/admin/lib/admin.py +++ b/admin/src/admin/lib/admin.py @@ -6,12 +6,17 @@ from .nextcloud import Nextcloud import logging as log from pprint import pprint +from .nextcloud_exc import * + 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']) + # self.internal={'users':self.get_mix_users(), + # 'groups':self.get_mix_groups(), + # 'roles':[]} self.external={'users':[], 'groups':[], 'roles':[]} @@ -21,18 +26,24 @@ class Admin(): # pprint(self.get_nextcloud_users()) def get_moodle_users(self): - users = self.moodle.get_users_with_groups_and_roles() - #self.moodle.get_user_by('email','%%')['users'] - return [{"id":u['id'], - "username":u['username'], - "first": u['firstname'], - "last": u['lastname'], - "email": u['email'], - "groups": u['groups'], - "roles": u['roles']} - for u in users] + return self.moodle.get_users_with_groups_and_roles() + + ## TOO SLOW. Not used. + # def get_moodle_users(self): + # log.warning('Loading moodle users... can take a long time...') + # users = self.moodle.get_users_with_groups_and_roles() + # #self.moodle.get_user_by('email','%%')['users'] + # return [{"id":u['id'], + # "username":u['username'], + # "first": u['firstname'], + # "last": u['lastname'], + # "email": u['email'], + # "groups": u['groups'], + # "roles": u['roles']} + # for u in users] def get_keycloak_users(self): + log.warning('Loading keycloak users... can take a long time...') users = self.keycloak.get_users_with_groups_and_roles() return [{"id":u['id'], "username":u['username'], @@ -44,6 +55,7 @@ class Admin(): for u in users] def get_nextcloud_users(self): + log.warning('Loading nextcloud users... can take a long time...') users = self.nextcloud.get_users_list() users_list=[] for user in users: @@ -57,10 +69,14 @@ class Admin(): "roles": []}) return users_list + # def get_ram_users(self): + # return self.internal['users'] + def get_mix_users(self): kusers=self.get_keycloak_users() musers=self.get_moodle_users() - nusers=self.get_nextcloud_users() + # nusers=self.get_nextcloud_users() + nusers=[] kusers_usernames=[u['username'] for u in kusers] musers_usernames=[u['username'] for u in musers] @@ -107,15 +123,21 @@ class Admin(): return self.keycloak.get_roles() def get_keycloak_groups(self): + log.warning('Loading keycloak groups... can take a long time...') return self.keycloak.get_groups() def get_moodle_groups(self): + log.warning('Loading moodle groups... can take a long time...') return self.moodle.get_cohorts() def get_nextcloud_groups(self): + log.warning('Loading nextcloud groups... can take a long time...') return self.nextcloud.get_groups_list() - def get_groups(self): + # def get_ram_groups(self): + # return self.internal['groups'] + + def get_mix_groups(self): kgroups=self.get_keycloak_groups() mgroups=self.get_moodle_groups() ngroups=self.get_nextcloud_groups() @@ -198,8 +220,29 @@ class Admin(): return True + def sync_external(self): + for u in self.external['users']: + log.error('Creating user: '+u['username']) + self.keycloak.add_user(u['username'],u['first'],u['last'],u['email'],'1Provaprovaprova',group=u['groups'][0]) - # with open(os.path.join(app.root_path, "../custom/jsons/google_all_imported_espinalt.json"),"r") as crt: - # pprint(data) - + def sync_to_moodle(self): + users=self.get_mix_users() + for u in users: + if not u['moodle']: + log.error('Creating moodle user: '+u['username']) + self.moodle.create_user(u['email'],u['username'],'-1Provaprovaprova',u['first'],u['last']) + def sync_to_nextcloud(self): + users=self.get_mix_users() + for u in users: + if not u['nextcloud']: + log.error('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...') + continue + except: + log.error(traceback.format_exc()) + #
Las contraseñas deben tener al menos 1 dígito(s).
Las contraseñas deben tener al menos 1 mayúscula(s).
Las contraseñas deben tener al menos 1 caracter(es) no alfanumérico(s) como *,-, diff --git a/admin/src/admin/lib/keycloak.py b/admin/src/admin/lib/keycloak.py index b209260..7776d03 100644 --- a/admin/src/admin/lib/keycloak.py +++ b/admin/src/admin/lib/keycloak.py @@ -37,6 +37,8 @@ class Keycloak(): realm_name=self.realm, verify=self.verify) +# 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 # try: @@ -65,26 +67,74 @@ class Keycloak(): self.connect() return self.keycloak_admin.get_users({}) +# q = """select u.username,u.email,u.first_name,u.last_name, u.realm_id, ua.value as quota +# ,json_agg(g."name") as group, json_agg(g_parent."name") as group_parent1, json_agg(g_parent2."name") as group_parent2 +# ,json_agg(r.name) as role +# from user_entity as u +# left join user_attribute as ua on ua.user_id=u.id and ua.name = 'quota' +# left join user_group_membership as ugm on ugm.user_id = u.id +# left join keycloak_group as g on g.id = ugm.group_id +# left join keycloak_group as g_parent on g.parent_group = g_parent.id +# left join keycloak_group as g_parent2 on g_parent.parent_group = g_parent2.id +# left join user_role_mapping as rm on rm.user_id = u.id +# left join keycloak_role as r on r.id = rm.role_id +# group by u.username,u.email,u.first_name,u.last_name, u.realm_id, ua.value +# order by u.username""" +# cur.execute(q) +# users = cur.fetchall() +# fields = [a.name for a in cur.description] +# cur.close() +# conn.close() + + +# users_with_lists = [list(l[:-4])+([[]] if l[-4] == [None] else [list(set(l[-4]))]) +\ +# ([[]] if l[-3] == [None] else [list(set(l[-3]))]) +\ +# ([[]] if l[-3] == [None] else [list(set(l[-2]))]) +\ +# ([[]] if l[-1] == [None] else [list(set(l[-1]))]) for l in users] + +# users_with_lists = [list(l[:-4])+([[]] if l[-4] == [None] else [list(set(l[-4]))]) +\ +# ([[]] if l[-3] == [None] else [list(set(l[-3]))]) +\ +# ([[]] if l[-3] == [None] else [list(set(l[-2]))]) +\ +# ([[]] if l[-1] == [None] else [list(set(l[-1]))]) for l in users_with_lists] + +# list_dict_users = [dict(zip(fields, r)) for r in users_with_lists] + + ## Too slow def get_users_with_groups_and_roles(self): self.connect() users=self.keycloak_admin.get_users({}) for user in users: - log.error(user) user['groups']=[g['path'] for g in self.keycloak_admin.get_user_groups(user_id=user['id'])] user['roles']= [r['name'] for r in self.keycloak_admin.get_realm_roles_of_user(user_id=user['id'])] return users - def add_user(self,username,first,last,email,password): + def add_user(self,username,first,last,email,password,group=False): # Returns user id + log.error('Creating group: '+str(group)) self.connect() - return self.keycloak_admin.create_user({"email": email, - "username": username, - "enabled": True, - "firstName": first, - "lastName": last, - "credentials":[{"type":"password", - "value":password, - "temporary":False}]}) + username=username.lower() + try: + uid=self.keycloak_admin.create_user({"email": email, + "username": username, + "enabled": True, + "firstName": first, + "lastName": last, + "credentials":[{"type":"password", + "value":password, + "temporary":False}]}) + except: + uid=self.keycloak_admin.get_user_id(username) + log.error(uid) + if 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)) + except: + self.keycloak_admin.create_group({"name":group}) + gid=self.keycloak_admin.get_group_by_path('/'+group)['id'] + log.error(gid) + self.keycloak_admin.group_user_add(uid,gid) + def add_user_role(self,client_id,user_id,role_id,role_name): self.connect() diff --git a/admin/src/admin/lib/moodle.py b/admin/src/admin/lib/moodle.py index f122a4a..413f56f 100644 --- a/admin/src/admin/lib/moodle.py +++ b/admin/src/admin/lib/moodle.py @@ -3,6 +3,9 @@ from admin import app import logging as log + +from .postgres import Postgres + # Module variables to connect to moodle api class Moodle(): @@ -20,7 +23,10 @@ class Moodle(): self.url = url self.endpoint = endpoint self.verify=verify - + + self.pg=Postgres('isard-apps-postgresql','moodle',app.config['MOODLE_POSTGRES_USER'],app.config['MOODLE_POSTGRES_PASSWORD']) + + def rest_api_parameters(self, in_args, prefix='', out_dict=None): """Transform dictionary/array structure to a flat dictionary, with key names defining the structure. @@ -70,14 +76,27 @@ class Moodle(): criteria = [{'key': key, 'value': value}] user = self.call('core_user_get_users', criteria=criteria) return user - + def get_users_with_groups_and_roles(self): - users=self.get_user_by('email','%%')['users'] - for user in users: - user['groups']=[c['name'] for c in self.get_user_cohorts(user['id'])] - user['roles']=[] - log.error(users) - return users + q = """select u.id as id, username, firstname as first, lastname as last, email, json_agg(h.name) as groups, json_agg(r.shortname) as roles + from mdl_user as u + LEFT JOIN mdl_cohort_members AS hm on hm.id = u.id + left join mdl_cohort AS h ON h.id = hm.cohortid + left join mdl_role_assignments AS ra ON ra.id = u.id + left join mdl_role as r on r.id = ra.roleid + group by u.id , username, first, last, email""" + (headers,users)=self.pg.select_with_headers(q) + users_with_lists = [list(l[:-2])+([[]] if l[-2] == [None] else [list(set(l[-2]))]) + ([[]] if l[-1] == [None] else [list(set(l[-1]))]) for l in users] + list_dict_users = [dict(zip(headers, r)) for r in users_with_lists] + return list_dict_users + + ## NOT USED. Too slow + # def get_users_with_groups_and_roles(self): + # users=self.get_user_by('email','%%')['users'] + # for user in users: + # user['groups']=[c['name'] for c in self.get_user_cohorts(user['id'])] + # user['roles']=[] + # return users def enroll_user_to_course(self, user_id, course_id, role_id=5): # 5 is student diff --git a/admin/src/admin/lib/nextcloud.py b/admin/src/admin/lib/nextcloud.py index bc0cb68..ce45e98 100644 --- a/admin/src/admin/lib/nextcloud.py +++ b/admin/src/admin/lib/nextcloud.py @@ -76,6 +76,27 @@ class Nextcloud(): raise # 100 - successful +# q = """select u.uid as username, adn.value as displayname, ade.value as email, json_agg(gg.displayname) as admin_groups,json_agg(g.displayname) as groups +# from oc_users as u +# left join oc_group_user as gu on gu.uid = u.uid +# left join oc_groups as g on gu.gid = g.gid +# left join oc_group_admin as ga on ga.uid = u.uid +# left join oc_groups as gg on gg.gid = ga.gid +# left join oc_accounts_data as adn on adn.uid = u.uid and adn.name = 'displayname' +# left join oc_accounts_data as ade on ade.uid = u.uid and ade.name = 'email' +# group by u.uid, adn.value, ade.value""" +# cur.execute(q) +# users = cur.fetchall() +# fields = [a.name for a in cur.description] +# cur.close() +# conn.close() + + +# users_with_lists = [list(l[:-2])+([[]] if l[-2] == [None] else [list(set(l[-2]))]) + ([[]] if l[-1] == [None] else [list(set(l[-1]))]) for l in users] +# users_with_lists = [list(l[:-2])+([[]] if l[-2] == [None] else [list(set(l[-2]))]) + ([[]] if l[-1] == [None] else [list(set(l[-1]))]) for l in users_with_lists] +# list_dict_users = [dict(zip(fields, r)) for r in users_with_lists] + + ### Too slow... def get_users_list(self): url = self.apiurl + "users?format=json" try: @@ -87,8 +108,11 @@ class Nextcloud(): log.error(traceback.format_exc()) raise - def add_user(self,userid,userpassword,quota,group='',email='',displayname=''): - data={'userid':userid,'password':userpassword,'quota':quota,'groups[]':group,'email':email,'displayname':displayname} + 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} url = self.apiurl + "users?format=json" headers = { 'Content-Type': 'application/x-www-form-urlencoded', @@ -98,7 +122,9 @@ class Nextcloud(): result = json.loads(self._request('POST',url,data=data,headers=headers)) if result['ocs']['meta']['statuscode'] == 100: return True if result['ocs']['meta']['statuscode'] == 102: raise ProviderItemExists - if result['ocs']['meta']['statuscode'] == 104: raise ProviderGroupNotExists + if result['ocs']['meta']['statuscode'] == 104: + self.add_group(group) + # raise ProviderGroupNotExists log.error('Get Nextcloud provider user add error: '+str(result)) raise ProviderOpError except: diff --git a/admin/src/admin/lib/postgres.py b/admin/src/admin/lib/postgres.py index b8ae12e..ab2f441 100644 --- a/admin/src/admin/lib/postgres.py +++ b/admin/src/admin/lib/postgres.py @@ -34,6 +34,12 @@ class Postgres(): self.conn.commit() # return self.cur.fetchall() + def select_with_headers(self,sql): + self.cur.execute(sql) + data=self.cur.fetchall() + fields = [a.name for a in self.cur.description] + return (fields,data) + # def update_moodle_saml_plugin(self): # plugin[('idpmetadata', 'NrtA5ynG0htowP3SXw7dBJRIAMxn-1PwuuXwOwNhlRwMIICmzCCAYMCBgF5jb0RCTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwNTIxMDcwMjI4WhcNMzEwNTIxMDcwNDA4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCI8xh/C0+frz3kgWiUbziTDls71R2YiXLSVE+bw7gbEgZUGCLhoEI679azMtIxmnzM/snIX+yTb12+XoYkgbiLTMPQfnH+Kiab6g3HL3KPfhqS+yWkFxOoCp6Ibmp7yPlVWuHH+MBfO8OBr/r8Ao7heFbuzjiLd1KG67rcoaxfDgMuBoEomg1bgEjFgHaQIrSC6OZzH0h987/arqufZXeXlfyiqScMPUi+u5IpDWSwz06UKP0k8mxzNSlpZ93CKOUSsV0SMLxqg7FQ3SGiOk577bGW9o9BDTkkmSo3Up6smc0LzwvvUwuNd0B1irGkWZFQN9OXJnJYf1InEebIMtmPAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADM34+qEGeBQ22luphVTuVJtGxcbxLx7DfsT0QfJD/OuxTTbNAa1VRyarb5juIAkqdj4y2quZna9ZXLecVo4RkwpzPoKoAkYA8b+kHnWqEwJi9iPrDvKb+GR0bBkLPN49YxIZ8IdKX/PRa3yuLHe+loiNsCaS/2ZK2KO46COsqU4QX1iVhF9kWphNLybjNAX45B6cJLsa1g0vXLdm3kv3SB4I2fErFVaOoDtFIjttoYlXdpUiThkPXBfr7N67P3dZHaS4tjJh+IZ8I6TINpcsH8dBkUhzYEIPHCePwSiC1w6WDBLNDuKt1mj1CZrLq+1x+Yhrs+QNRheEKGi89HZ8N0=urn:oasis:names:tc:SAML:2.0:nameid-format:persistenturn:oasis:names:tc:SAML:2.0:nameid-format:transienturn:oasis:names:tc:SAML:1.1:nameid-format:unspecifiedurn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress')] # pg_update = """UPDATE mdl_config_plugins set title = %s where plugin = auth_saml2 and name =""" diff --git a/admin/src/admin/static/js/external.js b/admin/src/admin/static/js/external.js index 362b898..c9a0da4 100644 --- a/admin/src/admin/static/js/external.js +++ b/admin/src/admin/static/js/external.js @@ -12,6 +12,24 @@ $(document).ready(function() { $('#modalImportForm')[0].reset(); }); + $('.btn-sync').on('click', function () { + $.ajax({ + type: "PUT", + url:"/isard-sso-admin/external", + success: function(data) + { + console.log('SUCCESS') + // $("#modalImport").modal('hide'); + // users_table.ajax.reload(); + // groups_table.ajax.reload(); + }, + error: function(data) + { + alert('Something went wrong on our side...') + } + }); + }); + $("#modalImport #send").on('click', function(e){ var form = $('#modalImportForm'); form.parsley().validate(); diff --git a/admin/src/admin/static/js/users.js b/admin/src/admin/static/js/users.js index 17130e1..0d2ff32 100644 --- a/admin/src/admin/static/js/users.js +++ b/admin/src/admin/static/js/users.js @@ -14,6 +14,42 @@ $(document).ready(function() { $('#modalAdd').parsley(); }); + $('.btn-sync_to_moodle').on('click', function () { + $.ajax({ + type: "POST", + url:"/isard-sso-admin/users/moodle", + success: function(data) + { + console.log('SUCCESS') + // $("#modalImport").modal('hide'); + // users_table.ajax.reload(); + // groups_table.ajax.reload(); + }, + error: function(data) + { + alert('Something went wrong on our side...') + } + }); + }); + + $('.btn-sync_to_nextcloud').on('click', function () { + $.ajax({ + type: "POST", + url:"/isard-sso-admin/users/nextcloud", + success: function(data) + { + console.log('SUCCESS') + // $("#modalImport").modal('hide'); + // users_table.ajax.reload(); + // groups_table.ajax.reload(); + }, + error: function(data) + { + alert('Something went wrong on our side...') + } + }); + }); + //DataTable Main renderer var table = $('#users').DataTable({ "ajax": { diff --git a/admin/src/admin/static/templates/pages/external.html b/admin/src/admin/static/templates/pages/external.html index f6a2e8d..7977142 100644 --- a/admin/src/admin/static/templates/pages/external.html +++ b/admin/src/admin/static/templates/pages/external.html @@ -15,6 +15,7 @@

External