new imports in admin

root 2021-06-01 14:06:25 +02:00
parent 9dc210d62d
commit d918eb7b6f
14 changed files with 449 additions and 45 deletions

View File

@ -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

View File

@ -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

View File

@ -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']:

View File

@ -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

View File

@ -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

View File

@ -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({
@ -36,6 +36,15 @@ $(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": [ {
@ -228,3 +238,46 @@ function populate_path(){
$(".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);
}
}
}

View File

@ -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>

View File

@ -47,6 +47,7 @@
<th>email</th>
<th>groups</th>
<th>roles</th>
<th>password</th>
</tr>
</thead>
<tbody>

View File

@ -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 %}

View File

@ -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():

5
admin/src/claus.py Normal file
View File

@ -0,0 +1,5 @@
import diceware
options = diceware.handle_options(None)
options.wordlist = 'cat_ascii'
options.num = 3
print(diceware.get_passphrase(options=options))

55
admin/src/postgres.py Normal file
View File

@ -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!")

134
admin/src/reset_pwds.py Normal file
View File

@ -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()

View File

@ -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="*")
@socketio.on('disconnect', namespace='/isard-sso-admin/sio')
def socketio_domains_disconnect():
None
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
if __name__ == '__main__':
socketio.run(app,host='0.0.0.0', port=9000, debug=False, cors_allowed_origins="*") #, logger=logger, engineio_logger=engineio_logger)