diff --git a/admin/src/admin/lib/admin.py b/admin/src/admin/lib/admin.py index 26ffa09..17cacef 100644 --- a/admin/src/admin/lib/admin.py +++ b/admin/src/admin/lib/admin.py @@ -18,12 +18,12 @@ options = diceware.handle_options(None) options.wordlist = 'cat_ascii' options.num = 3 +from .events import Events - -from flask_socketio import SocketIO, emit, join_room, leave_room, \ - close_room, rooms, disconnect, send -socketio = SocketIO(app) -from ..views.Socketio import socketio +# from flask_socketio import SocketIO, emit, join_room, leave_room, \ +# close_room, rooms, disconnect, send +# socketio = SocketIO(app) +# from ..views.Socketio import socketio # socketio = SocketIO(app) class Admin(): @@ -465,26 +465,76 @@ class Admin(): def get_external_roles(self): return self.external['roles'] - def upload_json(self,data): + def upload_csv_ug(self,data): + log.warning('Processing uploaded users...') + users=[] + total=len(data['data']) + item=1 + ev=Events('Processing uploaded users',total=len(data['data'])) + groups=[] + for u in data['data']: + log.warning('Processing ('+str(item)+'/'+str(total)+') uploaded user: '+u['username']) + user_groups=["/" + g.strip() for g in u['groups'].split(',')] + + groups=groups+user_groups + users.append({'provider':'external', + 'id':u['id'].strip(), + 'email': u['email'].strip(), + 'first': u['firstname'].strip(), + 'last': u['lastname'].strip(), + 'username': u['username'].strip(), + 'groups':user_groups, + 'roles':[u['role'].strip()], + 'password':diceware.get_passphrase(options=options)}) + item+=1 + ev.increment({'name':u['username'].split('@')[0]}) + self.external['users']=users + + groups=list(dict.fromkeys(groups)) + + sysgroups=[] + for g in groups: + sysgroups.append({'provider':'external', + "id": g, + "mailid": g, + "name": g, + "description": 'Imported with csv'}) + self.external['groups']=sysgroups + return True + + def upload_json_ga(self,data): groups=[] log.warning('Processing uploaded groups...') + ev=Events('Processing uploaded groups',total=len(data['data']['groups'])) for g in data['data']['groups']: - # for m in data['data']['d_members']: - - # data['provider'] - groups.append({'provider':'external', - "id": g['id'], - "mailid": g['email'].split('@')[0], - "name": g['name'], - "description": g['description']}) + try: + ev.increment({'name':g['name']}) + groups.append({'provider':'external', + "id": g['id'], + "mailid": g['email'].split('@')[0], + "name": g['name'], + "description": g['description']}) + except: + pass self.external['groups']=groups log.warning('Processing uploaded users...') users=[] total=len(data['data']['users']) - i=1 + item=1 + ev=Events('Processing uploaded users',total=len(data['data']['users'])) for u in data['data']['users']: - log.warning('Processing ('+str(i)+'/'+str(total)+') uploaded user: '+u['primaryEmail'].split('@')[0]) + log.warning('Processing ('+str(item)+'/'+str(total)+') uploaded user: '+u['primaryEmail'].split('@')[0]) + + # self.e.send({'event':'progress', + # 'id':u['id'], + # 'item':'user', + # 'action':'passwdgen', + # 'name':u['primaryEmail'].split('@')[0], + # 'progress':str(item)+'/'+str(total), + # 'status':True, + # 'msg':'Generating password', + # 'payload':None}) # data['provider'] users.append({'provider':'external', 'id':u['id'], @@ -494,12 +544,14 @@ class Admin(): 'username': u['primaryEmail'].split('@')[0], 'groups':[u['orgUnitPath']], ## WARNING: Removing the first 'roles':[], - 'password':diceware.get_passphrase(options=options)}) - socketio.emit('update', - json.dumps({'status':True,'item':'user','action':'delete','itemdata':''}), - namespace='/isard-sso-admin/sio', - room='admin') - i=i+1 + 'password': diceware.get_passphrase(options=options)}) + # app.socketio.emit('update', + # json.dumps({'status':True,'item':'user','action':'delete','itemdata':''}), + # namespace='/isard-sso-admin/sio', + # room='admin') + # sleep(0) + item+=1 + ev.increment({'name':u['primaryEmail'].split('@')[0]}) self.external['users']=users ## Add groups to users (now they only have their orgUnitPath) @@ -520,6 +572,7 @@ class Admin(): # log.warning('All syncs finished') def sync_to_keycloak(self): ### This one works from the external, moodle and nextcloud from the internal + groups=[] for u in self.external['users']: groups=groups+u['groups'] @@ -527,18 +580,22 @@ class Admin(): total=len(groups) i=0 + ev=Events('Syncing import groups to keycloak',total=len(groups)) for g in groups: i=i+1 # print('ADDING FULL PATH: '+str(g)) log.warning(' KEYCLOAK GROUPS: Adding group ('+str(i)+'/'+str(total)+'): '+g) + ev.increment({'name':g}) self.keycloak.add_group_tree(g) total=len(self.external['users']) index=0 + ev=Events('Syncing import users to keycloak',total=len(self.external['users'])) for u in self.external['users']: index=index+1 # Add user log.warning(' KEYCLOAK USERS: Adding user ('+str(index)+'/'+str(total)+'): '+u['username']) + ev.increment({'name':u['username']}) uid=self.keycloak.add_user(u['username'],u['first'],u['last'],u['email'],u['password']) # Add user to role and group rolename @@ -573,12 +630,14 @@ class Admin(): ### Create all groups. Skip / in system groups total=len(groups) i=0 + ev=Events('Syncing groups from keycloak to moodle',total=len(groups)) for g in groups: parts=g.split('/') subpath='' for i in range(1,len(parts)): try: log.warning(' MOODLE GROUPS: Adding group as cohort ('+str(i)+'/'+str(total)+'): '+subpath) + ev.increment({'name':subpath}) if parts[i] in ['admin','manager','teacher','student']: subpath=parts[i] else: @@ -592,19 +651,28 @@ class Admin(): cohorts=self.moodle.get_cohorts() ### Create users in moodle + ev=Events('Syncing users from keycloak to moodle',total=len(self.internal['users'])) for u in self.internal['users']: if not u['moodle']: - log.error('Creating moodle user: '+u['username']) - user=self.moodle.create_user(u['email'],u['username'],'1Random 1String',u['first'],u['last'])[0] + log.warning('Creating moodle user: '+u['username']) + ev.increment({'name':u['username']}) + if u['first'] == '': u['first']=' ' + if u['last'] == '': u['last']=' ' + try: + # pprint(u) + pprint(self.moodle.create_user(u['email'],u['username'],'1Random 1String',u['first'],u['last'])[0]) + except: + log.error(' -->> Error creating on moodle the user: '+u['username']) # user_id=user['id'] self.resync_data() ### Add user to their cohorts (groups) + ev=Events('Syncing users groups from keycloak to moodle cohorts',total=len(self.internal['users'])) for u in self.internal['users']: total=len(u['keycloak_groups']) index=0 + ev.increment({'name':u['username']}) for g in u['keycloak_groups']: - log.error('for user groups') parts=g.split('/') subpath='' # pprint(parts) @@ -640,18 +708,21 @@ class Admin(): total=len(groups) i=0 + ev=Events('Syncing groups from keycloak to nextcloud',total=len(groups)) for g in groups: parts=g.split('/') subpath='' for i in range(1,len(parts)): try: log.warning(' NEXTCLOUD GROUPS: Adding group ('+str(i)+'/'+str(total)+'): '+subpath) + ev.increment({'name':subpath}) subpath=subpath+'/'+parts[i] self.nextcloud.add_group(subpath) except: log.error('probably exists') i=i+1 + ev=Events('Syncing users from keycloak to nextcloud',total=len(self.internal['users'])) for u in self.internal['users']: # print('User '+u['username']) if not u['nextcloud']: @@ -659,6 +730,7 @@ class Admin(): log.warning(' NEXTCLOUD USERS: Creating nextcloud user: '+u['username']+' in groups '+str(u['keycloak_groups'])) # group=u['keycloak_groups'][0] if len(u['keycloak_groups']) else False try: + ev.increment({'name':u['username']}) self.nextcloud.add_user_with_groups(u['username'],'1Random 1String',500000000000,u['keycloak_groups'],u['email'],u['first']+' '+u['last']) except ProviderItemExists: log.warning('User '+u['username']+' already exists. Skipping...') @@ -678,7 +750,7 @@ class Admin(): log.info(' KEYCLOAK USERS: Removing user ('+str(i)+'/'+str(total)+'): '+u['username']) try: self.keycloak.delete_user(u['id']) - socketio.emit('update', + app.socketio.emit('update', json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}), namespace='/isard-sso-admin/sio', room='admin') @@ -692,7 +764,7 @@ class Admin(): log.info('Removing nextcloud user: '+u['username']) try: self.nextcloud.delete_user(u['nextcloud_id']) - socketio.emit('update', + app.socketio.emit('update', json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}), namespace='/isard-sso-admin/sio', room='admin') @@ -711,7 +783,7 @@ class Admin(): log.warning('Removing moodle users: '+','.join(usernames)) try: self.moodle.delete_users(userids) - socketio.emit('update', + app.socketio.emit('update', json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}), namespace='/isard-sso-admin/sio', room='admin') diff --git a/admin/src/admin/lib/events.py b/admin/src/admin/lib/events.py new file mode 100644 index 0000000..6f622e4 --- /dev/null +++ b/admin/src/admin/lib/events.py @@ -0,0 +1,73 @@ +#!flask/bin/python +# coding=utf-8 +from admin import app +import logging as log +import traceback + +from uuid import uuid4 +import json +from time import sleep +import sys,os +from flask import render_template, Response, request, redirect, url_for, jsonify +from flask_socketio import SocketIO, emit, join_room, leave_room, \ + close_room, rooms, disconnect, send +import base64 + +class Events(): + def __init__(self,title,text='',total=0): + self.eid=str(base64.b64encode(os.urandom(32))[:8]) + self.title=title + self.text=text + self.total=total + self.item=0 + self.create() + + def create(self): + app.socketio.emit('notify-create', + json.dumps({'id':self.eid, + 'title':self.title, + 'text':self.text}), + namespace='/isard-sso-admin/sio', + room='admin') + sleep(0.1) + + def __del__(self): + app.socketio.emit('notify-destroy', + json.dumps({'id':self.eid}), + namespace='/isard-sso-admin/sio', + room='admin') + sleep(0.1) + + def update_text(self,text): + self.text=text + app.socketio.emit('notify-update', + json.dumps({'id':self.eid, + 'text':self.text,}), + namespace='/isard-sso-admin/sio', + room='admin') + sleep(0.1) + + def increment(self,data={}): + self.item+=1 + app.socketio.emit('notify-update', + json.dumps({'id':self.eid, + 'text': '['+str(self.item)+'/'+str(self.total)+'] ', + 'item':self.item, + 'total':self.total, + 'data':data}), + namespace='/isard-sso-admin/sio', + room='admin') + sleep(0.1) + + def decrement(self,data={}): + self.item-=1 + app.socketio.emit('notify-update', + json.dumps({'id':self.eid, + 'text':'['+str(self.item)+'/'+str(self.total)+'] ', + 'item':self.item, + 'total':self.total, + 'data':data}), + namespace='/isard-sso-admin/sio', + room='admin') + sleep(0.1) + diff --git a/admin/src/admin/static/js/external.js b/admin/src/admin/static/js/external.js index 932d44b..a01a8b1 100644 --- a/admin/src/admin/static/js/external.js +++ b/admin/src/admin/static/js/external.js @@ -17,7 +17,7 @@ $(document).ready(function() { $.each(users_table.rows().data(),function(key, value){ ids[value['id']]=value['roles'] }); - console.log(ids) + // console.log(ids) $.ajax({ type: "PUT", url:"/isard-sso-admin/external", @@ -46,25 +46,30 @@ $(document).ready(function() { }) $("#modalImport #send").on('click', function(e){ - console.log(users_table.rows().data()) + // console.log(users_table.rows().data()) var form = $('#modalImportForm'); form.parsley().validate(); if (form.parsley().isValid()){ formdata = form.serializeObject() - formdata['data']=JSON.parse(filecontents) + if($('#format').val() == 'csv-ug'){ + formdata['data']=parseCSV(filecontents) + }else{ + formdata['data']=JSON.parse(filecontents) + } $.ajax({ type: "POST", url:"/isard-sso-admin/external", data: JSON.stringify(formdata), success: function(data) { - $("#modalImport").modal('hide'); - users_table.ajax.reload(); - groups_table.ajax.reload(); + console.log('SUCCESS') + // $("#modalImport").modal('hide'); + // users_table.ajax.reload(); + // groups_table.ajax.reload(); }, error: function(data) { - alert('Something went wrong on our side...') + console.log('Ajax timeout') } }); } @@ -194,20 +199,46 @@ $(document).ready(function() { }); function readFile (evt) { - path = ""; - items = []; - var files = evt.target.files; - var file = files[0]; - var reader = new FileReader(); - reader.onload = function(event) { - filecontents=event.target.result; - $.each(JSON.parse(filecontents), walker); - populate_path(items) + if($('#format').val() == 'json-ga'){ + path = ""; + items = []; + var files = evt.target.files; + var file = files[0]; + var reader = new FileReader(); + reader.onload = function(event) { + filecontents=event.target.result; + $.each(JSON.parse(filecontents), walker); + populate_path(items) + } + reader.readAsText(file, 'UTF-8') + } + if($('#format').val() == 'csv-ug'){ + var files = evt.target.files; + var file = files[0]; + var reader = new FileReader(); + reader.onload = function(event) { + filecontents=event.target.result; + // $.each(JSON.parse(filecontents), walker); + // populate_path(items) + } + reader.readAsText(file, 'UTF-8') } - reader.readAsText(file, 'UTF-8') } +function parseCSV(){ + lines=filecontents.split('\n') + header=lines[0].split(';') + users=[] + $.each(lines, function(n, l){ + if(n!=0 && l.length > 10){ + usr=toObject(header,l.split(';')) + usr['id']=usr['username'] + users.push(usr) + } + }) + return users; +} function toObject(names, values) { var result = {}; for (var i = 0; i < names.length; i++) diff --git a/admin/src/admin/static/js/status_socket.js b/admin/src/admin/static/js/status_socket.js index 7053b12..c710b7b 100644 --- a/admin/src/admin/static/js/status_socket.js +++ b/admin/src/admin/static/js/status_socket.js @@ -1,19 +1,86 @@ - // SocketIO - socket = io.connect(location.protocol+'//' + document.domain +'/isard-sso-admin/sio'); - console.log(location.protocol+'//' + document.domain +'/isard-sso-admin/sio') - socket.on('connect', function() { - // connection_done(); - console.log('Listening status socket'); - }); +notice={} +$lost=0; - socket.on('connect_error', function(data) { - // connection_lost(); - }); - - socket.on('update', function(data) { - var data = JSON.parse(data); - console.log('Status update') - console.log(data) - // var data = JSON.parse(data); - // drawUserQuota(data); +socket = io.connect(location.protocol+'//' + document.domain +'/isard-sso-admin/sio'); +console.log(location.protocol+'//' + document.domain +'/isard-sso-admin/sio') +socket.on('connect', function() { + if($lost){location.reload();} + console.log('Listening status socket'); +}); + +socket.on('connect_error', function(data) { + $lost=$lost+1; + $('#modal-lostconnection').modal({ + backdrop: 'static', + keyboard: false + }).modal('show'); +}); + +socket.on('notify-create', function(data) { + var data = JSON.parse(data); + notice[data.id] = new PNotify({ + title: data.title, + text: data.text, + hide: false }); +}); + +socket.on('notify-destroy', function(data) { + var data = JSON.parse(data); + notice[data.id].remove() +}); + +socket.on('notify-update', function(data) { + var data = JSON.parse(data); + // console.log(data.text) + notice[data.id].update({ + text: data.text + }) +}); + + // new PNotify({ + // title: "Quota for creating desktops full.", + // text: "Can't create another desktop, user quota full.", + // hide: true, + // delay: 3000, + // icon: 'fa fa-alert-sign', + // opacity: 1, + // type: 'error' + // }); + +// socket.on('update', function(data) { +// var data = JSON.parse(data); +// console.log('Status update') +// console.log(data) +// // var data = JSON.parse(data); +// // drawUserQuota(data); +// }); + +socket.on('update', function(data) { + var data = JSON.parse(data); + console.log('Status update') + console.log(data) + // var data = JSON.parse(data); + // drawUserQuota(data); +}); + + // {'event':'traceback', + // 'id':u['id'], + // 'item':'group', + // 'action':'add' + // 'name':g['name'], + // 'progress':str(item)+'/'+str(total), + // 'status':False, + // 'msg':, + // 'payload':{'traceback':traceback.format_exc(), + // 'data':g}) + +socket.on('progress', function(data) { + var data = JSON.parse(data); + console.log(data) + // $('.modal-progress #item').html(data.item) +}); + + + +//// diff --git a/admin/src/admin/static/templates/base.html b/admin/src/admin/static/templates/base.html index 2a2558c..92455a4 100644 --- a/admin/src/admin/static/templates/base.html +++ b/admin/src/admin/static/templates/base.html @@ -59,26 +59,9 @@ -