new imports in admin
parent
9dc210d62d
commit
d918eb7b6f
|
@ -14,6 +14,8 @@ RUN apk del .build_deps
|
|||
|
||||
RUN apk add --no-cache curl py3-yaml yarn libpq openssl
|
||||
|
||||
RUN wget -O /usr/lib/python3.8/site-packages/diceware/wordlists/wordlist_cat_ascii.txt https://raw.githubusercontent.com/1ma/diceware-cat/master/cat-wordlist-ascii.txt
|
||||
|
||||
# SSH configuration
|
||||
ARG SSH_ROOT_PWD
|
||||
RUN apk add openssh
|
||||
|
|
|
@ -17,7 +17,7 @@ zope.interface==5.1.0
|
|||
psycopg2==2.8.6
|
||||
Flask-SocketIO==2.8.6
|
||||
mysql-connector-python==8.0.25
|
||||
|
||||
diceware==0.9.6
|
||||
|
||||
#Flask-SocketIO==2.8.6
|
||||
#gevent==1.4.0
|
||||
|
|
|
@ -13,9 +13,18 @@ from pprint import pprint
|
|||
import traceback, os, json
|
||||
from time import sleep
|
||||
|
||||
import diceware
|
||||
options = diceware.handle_options(None)
|
||||
options.wordlist = 'cat_ascii'
|
||||
options.num = 3
|
||||
|
||||
|
||||
|
||||
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():
|
||||
def __init__(self):
|
||||
|
@ -407,6 +416,7 @@ class Admin():
|
|||
|
||||
def upload_json(self,data):
|
||||
groups=[]
|
||||
log.warning('Processing uploaded groups...')
|
||||
for g in data['data']['groups']:
|
||||
# for m in data['data']['d_members']:
|
||||
|
||||
|
@ -418,8 +428,12 @@ class Admin():
|
|||
"description": g['description']})
|
||||
self.external['groups']=groups
|
||||
|
||||
log.warning('Processing uploaded users...')
|
||||
users=[]
|
||||
total=len(data['data']['users'])
|
||||
i=1
|
||||
for u in data['data']['users']:
|
||||
log.warning('Processing ('+str(i)+'/'+str(total)+') uploaded user: '+u['primaryEmail'].split('@')[0])
|
||||
# data['provider']
|
||||
users.append({'provider':'external',
|
||||
'id':u['id'],
|
||||
|
@ -427,8 +441,14 @@ class Admin():
|
|||
'first': u['name']['givenName'],
|
||||
'last': u['name']['familyName'],
|
||||
'username': u['primaryEmail'].split('@')[0],
|
||||
'groups':[u['orgUnitPath'][1:]], ## WARNING: Removing the first
|
||||
'roles':[]})
|
||||
'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
|
||||
self.external['users']=users
|
||||
|
||||
## Add groups to users (now they only have their orgUnitPath)
|
||||
|
@ -443,23 +463,84 @@ class Admin():
|
|||
pprint(ids)
|
||||
log.warning('Starting sync to keycloak')
|
||||
self.sync_to_keycloak()
|
||||
log.warning('Starting sync to moodle')
|
||||
self.sync_to_moodle()
|
||||
log.warning('Starting sync to nextcloud')
|
||||
self.sync_to_nextcloud()
|
||||
log.warning('All syncs finished')
|
||||
# log.warning('Starting sync to moodle')
|
||||
# self.sync_to_moodle()
|
||||
# log.warning('Starting sync to nextcloud')
|
||||
# self.sync_to_nextcloud()
|
||||
# log.warning('All syncs finished')
|
||||
|
||||
def sync_to_keycloak(self):
|
||||
def sync_to_keycloak(self): ### This one works from the external, moodle and nextcloud from the internal
|
||||
groups=[]
|
||||
for u in self.external['users']:
|
||||
log.info('Creating user: '+u['username'])
|
||||
self.keycloak.add_user_with_groups_and_role(u['username'],u['first'],u['last'],u['email'],'1Provaprovaprova',u['roles'],u['groups'])
|
||||
# self.keycloak.add_user(u['username'],u['first'],u['last'],u['email'],'1Provaprovaprova',group=u['groups'][0])
|
||||
groups=groups+u['groups']
|
||||
groups=list(dict.fromkeys(groups))
|
||||
|
||||
total=len(groups)
|
||||
i=0
|
||||
for g in groups:
|
||||
i=i+1
|
||||
# print('ADDING FULL PATH: '+str(g))
|
||||
log.warning(' KEYCLOAK GROUPS: Adding group ('+str(i)+'/'+str(total)+'): '+g)
|
||||
self.keycloak.add_group_tree(g)
|
||||
|
||||
total=len(self.external['users'])
|
||||
index=0
|
||||
for u in self.external['users']:
|
||||
index=index+1
|
||||
# Add user
|
||||
log.warning(' KEYCLOAK USERS: Adding user ('+str(index)+'/'+str(total)+'): '+u['username'])
|
||||
uid=self.keycloak.add_user(u['username'],u['first'],u['last'],u['email'],u['password'])
|
||||
|
||||
# Add user to role and group rolename
|
||||
if len(u['roles']) != 0:
|
||||
log.warning(' KEYCLOAK USERS: Assign user '+u['username']+' with initial pwd '+ u['password']+' to role '+u['roles'][0])
|
||||
self.keycloak.assign_realm_roles(uid,u['roles'][0])
|
||||
gid=self.keycloak.get_group(path='/'+u['roles'][0])['id']
|
||||
self.keycloak.group_user_add(uid,gid)
|
||||
# Add user to groups
|
||||
for g in u['groups']:
|
||||
parts=g.split('/')
|
||||
sub=''
|
||||
if len(parts)==0:
|
||||
log.warning(' KEYCLOAK USERS: Skip assign user '+u['username']+' to any group as des not have one')
|
||||
continue # NO GROUP
|
||||
for i in range(1,len(parts)):
|
||||
sub=sub+'/'+parts[i]
|
||||
|
||||
if sub=='/': continue # User with no path
|
||||
log.warning(' KEYCLOAK USERS: Assign user '+u['username']+' to group '+ str(sub))
|
||||
gid=self.keycloak.get_group(path=sub)['id']
|
||||
self.keycloak.group_user_add(uid,gid)
|
||||
self.resync_data()
|
||||
|
||||
def sync_to_moodle(self): # works from the internal (keycloak)
|
||||
groups=[]
|
||||
for u in self.internal['users']:
|
||||
groups=groups+u['keycloak_groups']
|
||||
groups=list(dict.fromkeys(groups))
|
||||
|
||||
pprint(groups)
|
||||
total=len(groups)
|
||||
i=0
|
||||
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)
|
||||
subpath=subpath+'/'+parts[i]
|
||||
self.moodle.add_system_cohort(subpath)
|
||||
except:
|
||||
log.error('probably exists')
|
||||
i=i+1
|
||||
# print('ADDING FULL PATH: '+str(g))
|
||||
return
|
||||
|
||||
|
||||
def sync_to_moodle(self):
|
||||
for u in self.internal['users']:
|
||||
if not u['moodle']:
|
||||
log.info('Creating moodle user: '+u['username'])
|
||||
self.moodle.create_user(u['email'],u['username'],'-1Provaprovaprova',u['first'],u['last'])
|
||||
self.moodle.create_user(u['email'],u['username'],u['password'],u['first'],u['last'])
|
||||
|
||||
def sync_to_nextcloud(self):
|
||||
for u in self.internal['users']:
|
||||
|
@ -467,7 +548,7 @@ class Admin():
|
|||
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'])
|
||||
self.nextcloud.add_user(u['username'],u['password'],2000000000000,group,u['email'],u['first']+' '+u['last'])
|
||||
except ProviderItemExists:
|
||||
log.info('User '+u['username']+' already exists. Skipping...')
|
||||
continue
|
||||
|
@ -476,12 +557,15 @@ class Admin():
|
|||
# <div>Las contraseñas deben tener al menos 1 dígito(s).</div><div>Las contraseñas deben tener al menos 1 mayúscula(s).</div><div>Las contraseñas deben tener al menos 1 caracter(es) no alfanumérico(s) como *,-,
|
||||
|
||||
def delete_keycloak_users(self):
|
||||
total=len(self.internal['users'])
|
||||
i=0
|
||||
for u in self.internal['users']:
|
||||
i=i+1
|
||||
if not u['keycloak']: continue
|
||||
# Do not remove admin users!!! What to do with managers???
|
||||
if 'admin' in u['roles']: continue
|
||||
if 'manager' in u['roles']: continue
|
||||
log.info('Removing keycloak user: '+u['username'])
|
||||
# if 'manager' in u['roles']: continue
|
||||
log.info(' KEYCLOAK USERS: Removing user ('+str(i)+'/'+str(total)+'): '+u['username'])
|
||||
try:
|
||||
self.keycloak.delete_user(u['id'])
|
||||
socketio.emit('update',
|
||||
|
@ -489,8 +573,7 @@ class Admin():
|
|||
namespace='/isard-sso-admin/sio',
|
||||
room='admin')
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
log.warning('Could not remove user: '+u['username'])
|
||||
log.warning(' KEYCLOAK USERS: Could not remove user: '+u['username'] +'. Probably already not exists.')
|
||||
|
||||
def delete_nextcloud_users(self):
|
||||
for u in self.internal['users']:
|
||||
|
|
|
@ -84,6 +84,35 @@ class KeycloakClient():
|
|||
left join keycloak_role as r on r.id = rm.role_id
|
||||
group by u.id,u.username,u.email,u.first_name,u.last_name, u.realm_id, ua.value
|
||||
order by u.username"""
|
||||
|
||||
# q = """select u.id, u.username, u.email, u.first_name, u.last_name, u.realm_id, ua.value as quota, g.id, g.path, g.name,
|
||||
# --,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.id,u.username,u.email,u.first_name,u.last_name, u.realm_id, ua.value
|
||||
# order by u.username"""
|
||||
|
||||
# q = """select u.id, u.username, u.email, u.first_name, u.last_name, u.realm_id, ua.value as quota
|
||||
# ,json_agg(g."name") as group_name,json_agg(g."id") as group_id,json_agg(g."path") as group_path
|
||||
# ,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.id,u.username,u.email,u.first_name,u.last_name, u.realm_id, ua.value
|
||||
# order by u.username"""
|
||||
(headers,users)=self.keycloak_pg.select_with_headers(q)
|
||||
|
||||
users_with_lists = [list(l[:-4])+([[]] if l[-4] == [None] else [list(set(l[-4]))]) +\
|
||||
|
@ -108,7 +137,7 @@ class KeycloakClient():
|
|||
# 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,group=False):
|
||||
def add_user(self,username,first,last,email,password,group=False,temporary=True):
|
||||
# Returns user id
|
||||
self.connect()
|
||||
username=username.lower()
|
||||
|
@ -120,7 +149,7 @@ class KeycloakClient():
|
|||
"lastName": last,
|
||||
"credentials":[{"type":"password",
|
||||
"value":password,
|
||||
"temporary":False}]})
|
||||
"temporary":temporary}]})
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
|
||||
|
@ -134,6 +163,13 @@ class KeycloakClient():
|
|||
self.keycloak_admin.group_user_add(uid,gid)
|
||||
return uid
|
||||
|
||||
def update_user_pwd(self,user_id='61092e24-cd67-4b50-baf9-b60e01f12bff',payload={},temporary=True):
|
||||
payload={"credentials":[{"type":"password",
|
||||
"value":'pepito',
|
||||
"temporary":temporary}]}
|
||||
self.connect()
|
||||
self.keycloak_admin.update_user( user_id, payload)
|
||||
|
||||
def remove_user_group(self,user_id,group_id):
|
||||
self.connect()
|
||||
pass
|
||||
|
@ -196,15 +232,31 @@ class KeycloakClient():
|
|||
|
||||
def add_group_tree(self,path):
|
||||
parts=path.split('/')
|
||||
sub=''
|
||||
for i in range(1,len(parts+1)):
|
||||
parent_path=None
|
||||
for i in range(1,len(parts)):
|
||||
# print('Adding group name '+parts[i]+' with parent path '+str(parent_path))
|
||||
try:
|
||||
if i == 1: parent_id=self.add_group(parts[i])
|
||||
self.add_group(parts[i],parent_path,skip_exists=True)
|
||||
except:
|
||||
# Main already exists?? What a fail!
|
||||
parent_id=self.get_group(parent_id)['id']
|
||||
if parent_path==None:
|
||||
parent_path='/'+parts[i]
|
||||
else:
|
||||
parent_path=self.get_group(parent_path)['path']
|
||||
parent_path=parent_path+parts[i]
|
||||
continue
|
||||
self.add_group(parts[i],parent_id)
|
||||
|
||||
if parent_path==None:
|
||||
parent_path='/'+parts[i]
|
||||
else:
|
||||
parent_path=parent_path+parts[i]
|
||||
|
||||
# try:
|
||||
# if i == 1: parent_id=self.add_group(parts[i])
|
||||
# except:
|
||||
# # Main already exists?? What a fail!
|
||||
# parent_id=self.get_group(parent_id)['id']
|
||||
# continue
|
||||
# self.add_group(parts[i],parent_id)
|
||||
|
||||
def add_user_with_groups_and_role(self,username,first,last,email,password,role,groups):
|
||||
## Add user
|
||||
|
@ -232,9 +284,12 @@ class KeycloakClient():
|
|||
thepath='/'+parts[i]
|
||||
else:
|
||||
thepath=parent_path+'/'+parts[i]
|
||||
if thepath=='/': continue
|
||||
if thepath=='/':
|
||||
print('Not adding the user '+username+' to any group as does not have any...')
|
||||
continue
|
||||
gid=self.get_group(path=thepath)['id']
|
||||
|
||||
print('Adding '+username+' with uuid: '+uid+' to group '+g+' with uuid: '+gid)
|
||||
self.keycloak_admin.group_user_add(uid,gid)
|
||||
|
||||
|
||||
|
@ -242,7 +297,7 @@ class KeycloakClient():
|
|||
parent_path=parent_path+'/'+parts[i]
|
||||
|
||||
|
||||
self.group_user_add(uid,gid)
|
||||
# self.group_user_add(uid,gid)
|
||||
|
||||
|
||||
## ROLES
|
||||
|
|
|
@ -130,6 +130,18 @@ class Moodle():
|
|||
cohort = self.call('core_cohort_create_cohorts', cohorts=data)
|
||||
return cohort
|
||||
|
||||
# def add_users_to_cohort(self,users,cohort):
|
||||
# criteria = [{'key': key, 'value': value}]
|
||||
# user = self.call('core_cohort_add_cohort_members', criteria=criteria)
|
||||
# return user
|
||||
|
||||
# def add_users_to_cohort(self,userid,cohortid):
|
||||
# members=[{'cohorttype':{'type':'system','value':cohortid},
|
||||
# 'usertype':{'type':'id','value':userid}}]
|
||||
# criteria = [{'key': key, 'value': value}]
|
||||
# user = self.call('core_cohort_add_cohort_members', criteria=criteria)
|
||||
# return user
|
||||
|
||||
def get_cohort_members(self, cohort_id):
|
||||
members = self.call('core_cohort_get_cohort_members', cohortids=[cohort_id])[0]['userids']
|
||||
return members
|
||||
|
|
|
@ -15,7 +15,7 @@ $(document).ready(function() {
|
|||
$('.btn-sync').on('click', function () {
|
||||
ids={}
|
||||
$.each(users_table.rows().data(),function(key, value){
|
||||
ids[value['id']]=value['role']
|
||||
ids[value['id']]=value['roles']
|
||||
});
|
||||
console.log(ids)
|
||||
$.ajax({
|
||||
|
@ -35,7 +35,16 @@ $(document).ready(function() {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('.btn-download').on('click', function () {
|
||||
|
||||
data=users_table.rows().data()
|
||||
csv_data='TYPE,EXTID,EMAIL,FIRST,LSAT,USERNAME,GROUPS,ROLE,PASSWORD'+ '\r\n'
|
||||
csv_data=csv_data+convertToCSV(data)
|
||||
console.log(csv_data)
|
||||
exportCSVFile(csv_data, 'users_data')
|
||||
})
|
||||
|
||||
$("#modalImport #send").on('click', function(e){
|
||||
console.log(users_table.rows().data())
|
||||
var form = $('#modalImportForm');
|
||||
|
@ -143,6 +152,7 @@ $(document).ready(function() {
|
|||
{ "data": "email", "width": "10px"},
|
||||
{ "data": "groups", "width": "10px"},
|
||||
{ "data": "roles", "width": "10px"},
|
||||
{ "data": "password", "width": "10px"},
|
||||
],
|
||||
"order": [[3, 'asc']],
|
||||
"columnDefs": [ {
|
||||
|
@ -227,4 +237,47 @@ function populate_path(){
|
|||
$.each(items, function(key, value) {
|
||||
$(".populate").append('<option value=' + value['path']+ '>' + value['path'] + '</option>');
|
||||
})
|
||||
}
|
||||
|
||||
function convertToCSV(objArray) {
|
||||
var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
|
||||
var str = '';
|
||||
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
var line = '';
|
||||
for (var index in array[i]) {
|
||||
if (line != '') line += ','
|
||||
|
||||
if (Array.isArray(array[i][index])){
|
||||
line += '"'+array[i][index]+'"'
|
||||
}else{
|
||||
line += array[i][index];
|
||||
}
|
||||
}
|
||||
|
||||
str += line + '\r\n';
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function exportCSVFile(csv, fileTitle) {
|
||||
var exportedFilenmae = fileTitle + '.csv' || 'export.csv';
|
||||
|
||||
var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
||||
if (navigator.msSaveBlob) { // IE 10+
|
||||
navigator.msSaveBlob(blob, exportedFilenmae);
|
||||
} else {
|
||||
var link = document.createElement("a");
|
||||
if (link.download !== undefined) { // feature detection
|
||||
// Browsers that support HTML5 download attribute
|
||||
var url = URL.createObjectURL(blob);
|
||||
link.setAttribute("href", url);
|
||||
link.setAttribute("download", exportedFilenmae);
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -111,6 +111,7 @@
|
|||
<script src="/isard-sso-admin/vendors/select2/dist/js/select2.full.min.js"></script>
|
||||
<!-- SocketIO -->
|
||||
<script src="/isard-sso-admin/static/vendor/socket.io-2.3.1.slim.js"></script>
|
||||
<script src="/isard-sso-admin/static/js/status_socket.js"></script>
|
||||
|
||||
<!-- isard initializers -->
|
||||
<script src="/isard-sso-admin/static/dd.js"></script>
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
<th>email</th>
|
||||
<th>groups</th>
|
||||
<th>roles</th>
|
||||
<th>password</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -75,5 +75,4 @@
|
|||
<script src="/isard-sso-admin/vendors/switchery/dist/switchery.min.js"></script>
|
||||
<!-- Desktops sse & modals -->
|
||||
<script src="/isard-sso-admin/static/js/users.js"></script>
|
||||
<script src="/isard-sso-admin/static/js/status_socket.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
from flask_socketio import SocketIO, emit, join_room, leave_room, \
|
||||
close_room, rooms, disconnect, send
|
||||
from admin import app
|
||||
import json
|
||||
|
||||
socketio = SocketIO(app)
|
||||
# from ...start import socketio
|
||||
|
||||
@socketio.on('connect', namespace='/isard-sso-admin/sio')
|
||||
def socketio_connect():
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import diceware
|
||||
options = diceware.handle_options(None)
|
||||
options.wordlist = 'cat_ascii'
|
||||
options.num = 3
|
||||
print(diceware.get_passphrase(options=options))
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
import pprint
|
||||
|
||||
import logging as log
|
||||
import traceback
|
||||
import yaml, json
|
||||
|
||||
import psycopg2
|
||||
|
||||
class Postgres():
|
||||
|
||||
def __init__(self,host,database,user,password):
|
||||
self.conn = psycopg2.connect(
|
||||
host=host,
|
||||
database=database,
|
||||
user=user,
|
||||
password=password)
|
||||
|
||||
|
||||
# def __del__(self):
|
||||
# self.cur.close()
|
||||
# self.conn.close()
|
||||
|
||||
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()
|
||||
# return self.cur.fetchall()
|
||||
|
||||
def select_with_headers(self,sql):
|
||||
self.cur = self.conn.cursor()
|
||||
self.cur.execute(sql)
|
||||
data=self.cur.fetchall()
|
||||
fields = [a.name for a in self.cur.description]
|
||||
self.cur.close()
|
||||
return (fields,data)
|
||||
|
||||
# def update_moodle_saml_plugin(self):
|
||||
# plugin[('idpmetadata', '<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.'+app.config['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>NrtA5ynG0htowP3SXw7dBJRIAMxn-1PwuuXwOwNhlRw</ds:KeyName><ds:X509Data><ds:X509Certificate>MIICmzCCAYMCBgF5jb0RCTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwNTIxMDcwMjI4WhcNMzEwNTIxMDcwNDA4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCI8xh/C0+frz3kgWiUbziTDls71R2YiXLSVE+bw7gbEgZUGCLhoEI679azMtIxmnzM/snIX+yTb12+XoYkgbiLTMPQfnH+Kiab6g3HL3KPfhqS+yWkFxOoCp6Ibmp7yPlVWuHH+MBfO8OBr/r8Ao7heFbuzjiLd1KG67rcoaxfDgMuBoEomg1bgEjFgHaQIrSC6OZzH0h987/arqufZXeXlfyiqScMPUi+u5IpDWSwz06UKP0k8mxzNSlpZ93CKOUSsV0SMLxqg7FQ3SGiOk577bGW9o9BDTkkmSo3Up6smc0LzwvvUwuNd0B1irGkWZFQN9OXJnJYf1InEebIMtmPAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADM34+qEGeBQ22luphVTuVJtGxcbxLx7DfsT0QfJD/OuxTTbNAa1VRyarb5juIAkqdj4y2quZna9ZXLecVo4RkwpzPoKoAkYA8b+kHnWqEwJi9iPrDvKb+GR0bBkLPN49YxIZ8IdKX/PRa3yuLHe+loiNsCaS/2ZK2KO46COsqU4QX1iVhF9kWphNLybjNAX45B6cJLsa1g0vXLdm3kv3SB4I2fErFVaOoDtFIjttoYlXdpUiThkPXBfr7N67P3dZHaS4tjJh+IZ8I6TINpcsH8dBkUhzYEIPHCePwSiC1w6WDBLNDuKt1mj1CZrLq+1x+Yhrs+QNRheEKGi89HZ8N0=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.santantoni.duckdns.org/auth/realms/master/protocol/saml/resolve" index="0"/><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.santantoni.duckdns.org/auth/realms/master/protocol/saml"/><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.santantoni.duckdns.org/auth/realms/master/protocol/saml"/><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.santantoni.duckdns.org/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.santantoni.duckdns.org/auth/realms/master/protocol/saml"/><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.santantoni.duckdns.org/auth/realms/master/protocol/saml"/><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.santantoni.duckdns.org/auth/realms/master/protocol/saml"/><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.santantoni.duckdns.org/auth/realms/master/protocol/saml"/></md:IDPSSODescriptor></md:EntityDescriptor></md:EntitiesDescriptor>')]
|
||||
# pg_update = """UPDATE mdl_config_plugins set title = %s where plugin = auth_saml2 and name ="""
|
||||
# cursor.execute(pg_update, (title, bookid))
|
||||
# connection.commit()
|
||||
# count = cursor.rowcount
|
||||
# print(count, "Successfully Updated!")
|
|
@ -0,0 +1,134 @@
|
|||
#!/usr/bin/env python
|
||||
import time ,os
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import logging as log
|
||||
import traceback
|
||||
import yaml, json
|
||||
from pprint import pprint
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
from keycloak import KeycloakAdmin
|
||||
from postgres import Postgres
|
||||
|
||||
import diceware
|
||||
options = diceware.handle_options(None)
|
||||
options.wordlist = 'cat_ascii'
|
||||
options.num = 3
|
||||
|
||||
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
|
||||
"""
|
||||
def __init__(self,
|
||||
url="http://isard-sso-keycloak:8080/auth/",
|
||||
username=os.environ['KEYCLOAK_USER'],
|
||||
password=os.environ['KEYCLOAK_PASSWORD'],
|
||||
realm='master',
|
||||
verify=True):
|
||||
self.url=url
|
||||
self.username=username
|
||||
self.password=password
|
||||
self.realm=realm
|
||||
self.verify=verify
|
||||
|
||||
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,
|
||||
username=self.username,
|
||||
password=self.password,
|
||||
realm_name=self.realm,
|
||||
verify=self.verify)
|
||||
|
||||
|
||||
def update_pwds(self):
|
||||
self.get_users()
|
||||
|
||||
def get_users(self):
|
||||
self.connect()
|
||||
users=self.get_users_with_groups_and_roles()
|
||||
userupdate=[]
|
||||
for u in users:
|
||||
if u['username'] not in ['admin'] and not u['username'].startswith('system_'):
|
||||
print('Generating password for user '+u['username'])
|
||||
userupdate.append({'id':u['id'],
|
||||
'username':u['username'],
|
||||
'password': 'pepito'})
|
||||
# diceware.get_passphrase(options=options)})
|
||||
with open("user_temp_passwd.csv"),"w") as csv:
|
||||
csv.write(self.parse_idp_metadata())
|
||||
for u in userupdate:
|
||||
print('Updating keycloak password for user '+u['username'])
|
||||
self.update_user_pwd(u['id'],u['password'])
|
||||
|
||||
def update_user_pwd(self,user_id,password,temporary=True):
|
||||
payload={"credentials":[{"type":"password",
|
||||
"value":password,
|
||||
"temporary":temporary}]}
|
||||
self.connect()
|
||||
self.keycloak_admin.update_user( user_id, payload)
|
||||
|
||||
|
||||
def get_users_with_groups_and_roles(self):
|
||||
q = """select u.id, 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.id,u.username,u.email,u.first_name,u.last_name, u.realm_id, ua.value
|
||||
order by u.username"""
|
||||
|
||||
# q = """select u.id, u.username, u.email, u.first_name, u.last_name, u.realm_id, ua.value as quota, g.id, g.path, g.name,
|
||||
# --,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.id,u.username,u.email,u.first_name,u.last_name, u.realm_id, ua.value
|
||||
# order by u.username"""
|
||||
|
||||
# q = """select u.id, u.username, u.email, u.first_name, u.last_name, u.realm_id, ua.value as quota
|
||||
# ,json_agg(g."name") as group_name,json_agg(g."id") as group_id,json_agg(g."path") as group_path
|
||||
# ,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.id,u.username,u.email,u.first_name,u.last_name, u.realm_id, ua.value
|
||||
# order by u.username"""
|
||||
(headers,users)=self.keycloak_pg.select_with_headers(q)
|
||||
|
||||
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(headers, r)) for r in users_with_lists]
|
||||
return list_dict_users
|
||||
|
||||
k=KeycloakClient()
|
||||
k.update_pwds()
|
|
@ -8,21 +8,22 @@ from flask_socketio import SocketIO, emit, join_room, leave_room, \
|
|||
import json
|
||||
from admin import app
|
||||
|
||||
socketio = SocketIO(app)
|
||||
socketio.init_app(app, cors_allowed_origins="*")
|
||||
|
||||
# from .admin.views.Socketio import *
|
||||
@socketio.on('connect', namespace='/isard-sso-admin/sio')
|
||||
def socketio_connect():
|
||||
join_room('admin')
|
||||
socketio.emit('update',
|
||||
json.dumps('Joined'),
|
||||
namespace='/isard-sso-admin/sio',
|
||||
room='admin')
|
||||
# socketio.init_app(app, cors_allowed_origins="*")
|
||||
|
||||
from admin.views.Socketio import *
|
||||
# socketio = SocketIO(app)
|
||||
# @socketio.on('connect', namespace='/isard-sso-admin/sio')
|
||||
# def socketio_connect():
|
||||
# join_room('admin')
|
||||
# socketio.emit('update',
|
||||
# json.dumps('Joined'),
|
||||
# namespace='/isard-sso-admin/sio',
|
||||
# room='admin')
|
||||
|
||||
@socketio.on('disconnect', namespace='/isard-sso-admin/sio')
|
||||
def socketio_domains_disconnect():
|
||||
None
|
||||
# @socketio.on('disconnect', namespace='/isard-sso-admin/sio')
|
||||
# def socketio_domains_disconnect():
|
||||
# None
|
||||
|
||||
if __name__ == '__main__':
|
||||
socketio.run(app,host='0.0.0.0', port=9000, debug=False, cors_allowed_origins="*") #, logger=logger, engineio_logger=engineio_logger)
|
||||
|
|
Loading…
Reference in New Issue