updated admin functs

root 2021-06-06 22:05:12 +02:00
parent a65441bc03
commit 90ef56bbdf
41 changed files with 1368 additions and 599 deletions

View File

@ -16,7 +16,7 @@ You can generate one with:
os.urandom(24) os.urandom(24)
And paste it here. And paste it here.
''' '''
app.secret_key = "Change this key!//\xf7\x83\xbe\x17\xfa\xa3zT\n\\]m\xa6\x8bF\xdd\r\xf7\x9e\x1d\x1f\x14'" app.secret_key = "Change this key!/\xf7\x83\xbe\x17\xfa\xa3zT\n\\]m\xa6\x8bF\xdd\r\xf7\x9e\x1d\x1f\x14'"
print('Starting isard-sso api...') print('Starting isard-sso api...')
@ -45,15 +45,15 @@ else:
''' '''
Serve static files Serve static files
''' '''
@app.route('/isard-sso-admin/build/<path:path>') @app.route('/build/<path:path>')
def send_build(path): def send_build(path):
return send_from_directory(os.path.join(app.root_path, 'node_modules/gentelella/build'), path) return send_from_directory(os.path.join(app.root_path, 'node_modules/gentelella/build'), path)
@app.route('/isard-sso-admin/vendors/<path:path>') @app.route('/vendors/<path:path>')
def send_vendors(path): def send_vendors(path):
return send_from_directory(os.path.join(app.root_path, 'node_modules/gentelella/vendors'), path) return send_from_directory(os.path.join(app.root_path, 'node_modules/gentelella/vendors'), path)
@app.route('/isard-sso-admin/templates/<path:path>') @app.route('/templates/<path:path>')
def send_templates(path): def send_templates(path):
return send_from_directory(os.path.join(app.root_path, 'templates'), path) return send_from_directory(os.path.join(app.root_path, 'templates'), path)
@ -61,14 +61,18 @@ def send_templates(path):
# def send_templates(path): # def send_templates(path):
# return send_from_directory(os.path.join(app.root_path, 'static/templates'), path) # return send_from_directory(os.path.join(app.root_path, 'static/templates'), path)
@app.route('/isard-sso-admin/static/<path:path>') @app.route('/static/<path:path>')
def send_static_js(path): def send_static_js(path):
return send_from_directory(os.path.join(app.root_path, 'static'), path) return send_from_directory(os.path.join(app.root_path, 'static'), path)
@app.route('/isard-sso-admin/avatars/<path:path>') @app.route('/avatars/<path:path>')
def send_avatars_img(path): def send_avatars_img(path):
return send_from_directory(os.path.join(app.root_path, '../avatars/master-avatars'), path) return send_from_directory(os.path.join(app.root_path, '../avatars/master-avatars'), path)
@app.route('/custom/<path:path>')
def send_custom(path):
return send_from_directory(os.path.join(app.root_path, '../custom'), path)
# @app.errorhandler(404) # @app.errorhandler(404)
# def not_found_error(error): # def not_found_error(error):
# return render_template('page_404.html'), 404 # return render_template('page_404.html'), 404
@ -81,8 +85,8 @@ def send_avatars_img(path):
Import all views Import all views
''' '''
from .views import LoginViews from .views import LoginViews
from .views import MenuViews from .views import WebViews
from .views import AvatarViews from .views import ApiViews

View File

@ -1,31 +1,28 @@
from admin import app from admin import app
from flask_login import LoginManager, UserMixin from flask_login import LoginManager, UserMixin
from flask_login import login_required
from flask_oidc import OpenIDConnect
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login"
app.config.update({
'SECRET_KEY': 'u\x91\xcf\xfa\x0c\xb9\x95\xe3t\xba2K\x7f\xfd\xca\xa3\x9f\x90\x88\xb8\xee\xa4\xd6\xe4',
'TESTING': True,
'DEBUG': True,
'OIDC_CLIENT_SECRETS': 'client_secrets.json',
'OIDC_ID_TOKEN_COOKIE_SECURE': False,
'OIDC_REQUIRE_VERIFIED_EMAIL': False,
'OIDC_VALID_ISSUERS': ['https://sso.santantoni.duckdns.org:8080/auth/realms/master'],
'OIDC_OPENID_REALM': 'https://sso.santantoni.duckdns.org/isard-sso-admin/custom_callback',
'OVERWRITE_REDIRECT_URI': 'https://sso.santantoni.duckdns.org/isard-sso-admin/custom_callback',
})
# 'OVERWRITE_REDIRECT_URI': 'https://sso.santantoni.duckdns.org/isard-sso-admin/custom_callback',
# 'OIDC_CALLBACK_ROUTE': '/isard-sso-admin/custom_callback'
oidc = OpenIDConnect(app)
import os import os
# login_manager = LoginManager() ''' OIDC TESTS '''
# login_manager.init_app(app) # from flask_oidc import OpenIDConnect
# app.config.update({
# 'SECRET_KEY': 'u\x91\xcf\xfa\x0c\xb9\x95\xe3t\xba2K\x7f\xfd\xca\xa3\x9f\x90\x88\xb8\xee\xa4\xd6\xe4',
# 'TESTING': True,
# 'DEBUG': True,
# 'OIDC_CLIENT_SECRETS': 'client_secrets.json',
# 'OIDC_ID_TOKEN_COOKIE_SECURE': False,
# 'OIDC_REQUIRE_VERIFIED_EMAIL': False,
# 'OIDC_VALID_ISSUERS': ['https://sso.santantoni.duckdns.org:8080/auth/realms/master'],
# 'OIDC_OPENID_REALM': 'https://sso.santantoni.duckdns.org//custom_callback',
# 'OVERWRITE_REDIRECT_URI': 'https://sso.santantoni.duckdns.org//custom_callback',
# })
# # 'OVERWRITE_REDIRECT_URI': 'https://sso.santantoni.duckdns.org//custom_callback',
# # 'OIDC_CALLBACK_ROUTE': '//custom_callback'
# oidc = OpenIDConnect(app)
''' OIDC TESTS '''
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login" login_manager.login_view = "login"
@ -33,12 +30,12 @@ ram_users={
os.environ["ADMINAPP_USER"]: { os.environ["ADMINAPP_USER"]: {
'id': os.environ["ADMINAPP_USER"], 'id': os.environ["ADMINAPP_USER"],
'password': os.environ["ADMINAPP_PASSWORD"], 'password': os.environ["ADMINAPP_PASSWORD"],
'role': 'admin' 'role': 'manager'
}, },
os.environ["KEYCLOAK_USER"]: { os.environ["KEYCLOAK_USER"]: {
'id': os.environ["KEYCLOAK_USER"], 'id': os.environ["KEYCLOAK_USER"],
'password': os.environ["KEYCLOAK_PASSWORD"], 'password': os.environ["KEYCLOAK_PASSWORD"],
'role': 'admin-keycloak', 'role': 'admin',
} }
} }

View File

@ -247,6 +247,7 @@ class Admin():
"username":u['username'], "username":u['username'],
"first": u.get('first_name',None), "first": u.get('first_name',None),
"last": u.get('last_name',None), "last": u.get('last_name',None),
"enabled": u['enabled'],
"email": u.get('email',''), "email": u.get('email',''),
"groups": u['group'], "groups": u['group'],
"roles": filter_roles_list(u['role'])} "roles": filter_roles_list(u['role'])}
@ -430,7 +431,7 @@ class Admin():
'username': u['username'].strip(), 'username': u['username'].strip(),
'groups':user_groups, 'groups':user_groups,
'roles':[u['role'].strip()], 'roles':[u['role'].strip()],
'password':diceware.get_passphrase(options=options)}) 'password': self.get_dice_pwd()})
item+=1 item+=1
ev.increment({'name':u['username'].split('@')[0]}) ev.increment({'name':u['username'].split('@')[0]})
self.external['users']=users self.external['users']=users
@ -447,13 +448,19 @@ class Admin():
self.external['groups']=sysgroups self.external['groups']=sysgroups
return True return True
def get_dice_pwd(self):
return diceware.get_passphrase(options=options)
def upload_json_ga(self,data): def upload_json_ga(self,data):
groups=[] groups=[]
log.warning('Processing uploaded groups...') log.warning('Processing uploaded groups...')
ev=Events('Processing uploaded groups',total=len(data['data']['groups'])) try:
ev=Events('Processing uploaded groups','Group:',total=len(data['data']['groups']))
except:
log.error(traceback.format_exc())
for g in data['data']['groups']: for g in data['data']['groups']:
try: try:
ev.increment({'name':g['name']}) ev.increment({'name':g['name'],'data':g})
groups.append({'provider':'external', groups.append({'provider':'external',
"id": g['id'], "id": g['id'],
"mailid": g['email'].split('@')[0], "mailid": g['email'].split('@')[0],
@ -467,10 +474,10 @@ class Admin():
users=[] users=[]
total=len(data['data']['users']) total=len(data['data']['users'])
item=1 item=1
ev=Events('Processing uploaded users',total=len(data['data']['users'])) ev=Events('Processing uploaded users','User:',total=len(data['data']['users']))
for u in data['data']['users']: for u in data['data']['users']:
log.warning('Processing ('+str(item)+'/'+str(total)+') uploaded user: '+u['primaryEmail'].split('@')[0]) log.warning('Processing ('+str(item)+'/'+str(total)+') uploaded user: '+u['primaryEmail'].split('@')[0])
users.append({'provider':'external', new_user={'provider':'external',
'id':u['id'], 'id':u['id'],
'email': u['primaryEmail'], 'email': u['primaryEmail'],
'first': u['name']['givenName'], 'first': u['name']['givenName'],
@ -478,9 +485,10 @@ class Admin():
'username': u['primaryEmail'].split('@')[0], 'username': u['primaryEmail'].split('@')[0],
'groups':[u['orgUnitPath']], ## WARNING: Removing the first 'groups':[u['orgUnitPath']], ## WARNING: Removing the first
'roles':[], 'roles':[],
'password': diceware.get_passphrase(options=options)}) 'password': diceware.get_passphrase(options=options)}
users.append(new_user)
item+=1 item+=1
ev.increment({'name':u['primaryEmail'].split('@')[0]}) ev.increment({'name':u['primaryEmail'].split('@')[0],'data':new_user})
self.external['users']=users self.external['users']=users
## Add groups to users (now they only have their orgUnitPath) ## Add groups to users (now they only have their orgUnitPath)
@ -495,14 +503,13 @@ class Admin():
log.warning('Starting sync to keycloak') log.warning('Starting sync to keycloak')
self.sync_to_keycloak() self.sync_to_keycloak()
### Now we only sycn external to keycloak and then they can be updated to others with UI buttons ### Now we only sycn external to keycloak and then they can be updated to others with UI buttons
# log.warning('Starting sync to moodle') log.warning('Starting sync to moodle')
# self.sync_to_moodle() self.sync_to_moodle()
# log.warning('Starting sync to nextcloud') log.warning('Starting sync to nextcloud')
# self.sync_to_nextcloud() self.sync_to_nextcloud()
# log.warning('All syncs finished') log.warning('All syncs finished')
def sync_to_keycloak(self): ### This one works from the external, moodle and nextcloud from the internal def sync_to_keycloak(self): ### This one works from the external, moodle and nextcloud from the internal
groups=[] groups=[]
for u in self.external['users']: for u in self.external['users']:
groups=groups+u['groups'] groups=groups+u['groups']
@ -510,7 +517,7 @@ class Admin():
total=len(groups) total=len(groups)
i=0 i=0
ev=Events('Syncing import groups to keycloak',total=len(groups)) ev=Events('Syncing import groups to keycloak','Adding group:',total=len(groups))
for g in groups: for g in groups:
i=i+1 i=i+1
log.warning(' KEYCLOAK GROUPS: Adding group ('+str(i)+'/'+str(total)+'): '+g) log.warning(' KEYCLOAK GROUPS: Adding group ('+str(i)+'/'+str(total)+'): '+g)
@ -519,12 +526,12 @@ class Admin():
total=len(self.external['users']) total=len(self.external['users'])
index=0 index=0
ev=Events('Syncing import users to keycloak',total=len(self.external['users'])) ev=Events('Syncing import users to keycloak','Adding user:',total=len(self.external['users']))
for u in self.external['users']: for u in self.external['users']:
index=index+1 index=index+1
# Add user # Add user
log.warning(' KEYCLOAK USERS: Adding user ('+str(index)+'/'+str(total)+'): '+u['username']) log.warning(' KEYCLOAK USERS: Adding user ('+str(index)+'/'+str(total)+'): '+u['username'])
ev.increment({'name':u['username']}) ev.increment({'name':u['username'],'data':u})
uid=self.keycloak.add_user(u['username'],u['first'],u['last'],u['email'],u['password']) uid=self.keycloak.add_user(u['username'],u['first'],u['last'],u['email'],u['password'])
# Add user to role and group rolename # Add user to role and group rolename
@ -538,11 +545,10 @@ class Admin():
parts=g.split('/') parts=g.split('/')
sub='' sub=''
if len(parts)==0: if len(parts)==0:
log.warning(' KEYCLOAK USERS: Skip assign user '+u['username']+' to any group as des not have one') log.warning(' KEYCLOAK USERS: Skip assign user '+u['username']+' to any group as does not have one')
continue # NO GROUP continue # NO GROUP
for i in range(1,len(parts)): for i in range(1,len(parts)):
sub=sub+'/'+parts[i] sub=sub+'/'+parts[i]
if sub=='/': continue # User with no path if sub=='/': continue # User with no path
log.warning(' KEYCLOAK USERS: Assign user '+u['username']+' to group '+ str(sub)) log.warning(' KEYCLOAK USERS: Assign user '+u['username']+' to group '+ str(sub))
gid=self.keycloak.get_group_by_path(path=sub)['id'] gid=self.keycloak.get_group_by_path(path=sub)['id']
@ -588,7 +594,6 @@ class Admin():
if u['first'] == '': u['first']=' ' if u['first'] == '': u['first']=' '
if u['last'] == '': u['last']=' ' if u['last'] == '': u['last']=' '
try: try:
# pprint(u)
pprint(self.moodle.create_user(u['email'],u['username'],'1Random 1String',u['first'],u['last'])[0]) pprint(self.moodle.create_user(u['email'],u['username'],'1Random 1String',u['first'],u['last'])[0])
except: except:
log.error(' -->> Error creating on moodle the user: '+u['username']) log.error(' -->> Error creating on moodle the user: '+u['username'])
@ -604,7 +609,6 @@ class Admin():
for g in u['keycloak_groups']: for g in u['keycloak_groups']:
parts=g.split('/') parts=g.split('/')
subpath='' subpath=''
# pprint(parts)
for i in range(1,len(parts)): for i in range(1,len(parts)):
if parts[i] in ['admin','manager','teacher','student']: if parts[i] in ['admin','manager','teacher','student']:
subpath=parts[i] subpath=parts[i]
@ -663,9 +667,24 @@ class Admin():
except: except:
log.error(traceback.format_exc()) log.error(traceback.format_exc())
def delete_keycloak_user(self,userid):
user=[u for u in self.internal['users'] if u['id']==userid]
if len(user) and user[0]['keycloak']:
user=user[0]
keycloak_id=user['id']
else:
return False
log.warning('Removing keycloak user: '+user['username'])
try:
self.keycloak.delete_user(keycloak_id)
except:
log.error(traceback.format_exc())
log.warning('Could not remove users: '+user['username'])
def delete_keycloak_users(self): def delete_keycloak_users(self):
total=len(self.internal['users']) total=len(self.internal['users'])
i=0 i=0
ev=Events('Deleting users from keycloak','Deleting user:',total=len(self.internal['users']))
for u in self.internal['users']: for u in self.internal['users']:
i=i+1 i=i+1
if not u['keycloak']: continue if not u['keycloak']: continue
@ -673,29 +692,53 @@ class Admin():
if ['admin'] in u['roles']: continue if ['admin'] in u['roles']: continue
log.info(' KEYCLOAK USERS: Removing user ('+str(i)+'/'+str(total)+'): '+u['username']) log.info(' KEYCLOAK USERS: Removing user ('+str(i)+'/'+str(total)+'): '+u['username'])
try: try:
ev.increment({'name':u['username'],'data':u})
self.keycloak.delete_user(u['id']) self.keycloak.delete_user(u['id'])
app.socketio.emit('update',
json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}),
namespace='/isard-sso-admin/sio',
room='admin')
except: except:
log.warning(' KEYCLOAK USERS: Could not remove user: '+u['username'] +'. Probably already not exists.') log.warning(' KEYCLOAK USERS: Could not remove user: '+u['username'] +'. Probably already not exists.')
def delete_nextcloud_user(self,userid):
user=[u for u in self.internal['users'] if u['id']==userid]
if len(user) and user[0]['nextcloud']:
user=user[0]
nextcloud_id=user['nextcloud_id']
else:
return False
log.warning('Removing nextcloud user: '+user['username'])
try:
self.nextcloud.delete_user(nextcloud_id)
except:
log.error(traceback.format_exc())
log.warning('Could not remove users: '+user['username'])
def delete_nextcloud_users(self): def delete_nextcloud_users(self):
ev=Events('Deleting users from nextcloud',total=len(self.internal['users']))
for u in self.internal['users']: for u in self.internal['users']:
if u['nextcloud'] and not u['keycloak']: if u['nextcloud'] and not u['keycloak']:
if u['roles'] and 'admin' in u['roles']: continue if u['roles'] and 'admin' in u['roles']: continue
log.info('Removing nextcloud user: '+u['username']) log.info('Removing nextcloud user: '+u['username'])
try: try:
ev.increment({'name':u['username']})
self.nextcloud.delete_user(u['nextcloud_id']) self.nextcloud.delete_user(u['nextcloud_id'])
app.socketio.emit('update',
json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}),
namespace='/isard-sso-admin/sio',
room='admin')
except: except:
log.error(traceback.format_exc()) log.error(traceback.format_exc())
log.warning('Could not remove user: '+u['username']) log.warning('Could not remove user: '+u['username'])
def delete_moodle_user(self,userid):
user=[u for u in self.internal['users'] if u['id']==userid]
if len(user) and user[0]['moodle']:
user=user[0]
moodle_id=user['moodle_id']
else:
return False
log.warning('Removing moodle user: '+user['username'])
try:
self.moodle.delete_users([moodle_id])
except:
log.error(traceback.format_exc())
log.warning('Could not remove users: '+user['username'])
def delete_moodle_users(self): def delete_moodle_users(self):
userids=[] userids=[]
usernames=[] usernames=[]
@ -709,13 +752,14 @@ class Admin():
self.moodle.delete_users(userids) self.moodle.delete_users(userids)
app.socketio.emit('update', app.socketio.emit('update',
json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}), json.dumps({'status':True,'item':'user','action':'delete','itemdata':u}),
namespace='/isard-sso-admin/sio', namespace='//sio',
room='admin') room='admin')
except: except:
log.error(traceback.format_exc()) log.error(traceback.format_exc())
log.warning('Could not remove users: '+','.join(usernames)) log.warning('Could not remove users: '+','.join(usernames))
def delete_keycloak_groups(self): def delete_keycloak_groups(self):
for g in self.internal['groups']: for g in self.internal['groups']:
if not g['keycloak']: continue if not g['keycloak']: continue
@ -734,3 +778,17 @@ class Admin():
if externaluser['id'] == newuserid: if externaluser['id'] == newuserid:
externaluser['roles']=[data['action']] externaluser['roles']=[data['action']]
return True return True
def user_update_password(self,userid,password,temporary):
return self.keycloak.update_user_pwd(userid,password,temporary)
def delete_user(self,userid):
log.warning('deleting user moodle, nextcloud keycloak')
self.delete_moodle_user(userid)
self.delete_nextcloud_user(userid)
self.delete_keycloak_user(userid)
self.resync_data()
return True
def get_user(self,userid):
return [u for u in self.internal['users'] if u['id']==userid][0]

View File

@ -52,4 +52,4 @@ class Avatars():
log.error(" AVATARS: Error occured when deleting avatar object", error) log.error(" AVATARS: Error occured when deleting avatar object", error)
def get_users_without_image(self,users): def get_users_without_image(self,users):
return [u for u in users if u['id'] not in self.minio_get_objects()] return [u for u in users if u['id'] and u['id'] not in self.minio_get_objects()]

View File

@ -23,51 +23,80 @@ class Events():
self.create() self.create()
def create(self): def create(self):
log.info('START '+self.eid+': '+self.text)
app.socketio.emit('notify-create', app.socketio.emit('notify-create',
json.dumps({'id':self.eid, json.dumps({'id':self.eid,
'title':self.title, 'title':self.title,
'text':self.text}), 'text':self.text}),
namespace='/isard-sso-admin/sio', namespace='/sio',
room='admin') room='admin')
sleep(0.1) sleep(0.001)
def __del__(self): def __del__(self):
log.info('END '+self.eid+': '+self.text)
app.socketio.emit('notify-destroy', app.socketio.emit('notify-destroy',
json.dumps({'id':self.eid}), json.dumps({'id':self.eid}),
namespace='/isard-sso-admin/sio', namespace='/sio',
room='admin') room='admin')
sleep(0.1) sleep(0.001)
def update_text(self,text): def update_text(self,text):
self.text=text self.text=text
app.socketio.emit('notify-update', app.socketio.emit('notify-update',
json.dumps({'id':self.eid, json.dumps({'id':self.eid,
'text':self.text,}), 'text':self.text,}),
namespace='/isard-sso-admin/sio', namespace='/sio',
room='admin') room='admin')
sleep(0.1) sleep(0.001)
def increment(self,data={}): def append_text(self,text):
self.text=self.text+'<br>'+text
app.socketio.emit('notify-update',
json.dumps({'id':self.eid,
'text':self.text,}),
namespace='/sio',
room='admin')
sleep(0.001)
def increment(self,data={'name':'','data':[]}):
self.item+=1 self.item+=1
log.info('INCREMENT '+self.eid+': '+self.text)
app.socketio.emit('notify-update', app.socketio.emit('notify-update',
json.dumps({'id':self.eid, json.dumps({'id':self.eid,
'text': '['+str(self.item)+'/'+str(self.total)+'] ', 'title':self.title,
'text': '['+str(self.item)+'/'+str(self.total)+'] '+self.text+' '+data['name'],
'item':self.item, 'item':self.item,
'total':self.total, 'total':self.total,
'data':data}), 'data':data}),
namespace='/isard-sso-admin/sio', namespace='/sio',
room='admin') room='admin')
sleep(0.1) sleep(0.0001)
def decrement(self,data={}): def decrement(self,data={'name':'','data':[]}):
self.item-=1 self.item-=1
log.info('DECREMENT '+self.eid+': '+self.text)
app.socketio.emit('notify-update', app.socketio.emit('notify-update',
json.dumps({'id':self.eid, json.dumps({'id':self.eid,
'text':'['+str(self.item)+'/'+str(self.total)+'] ', 'title':self.title,
'text': '['+str(self.item)+'/'+str(self.total)+'] '+self.text+' '+data['name'],
'item':self.item, 'item':self.item,
'total':self.total, 'total':self.total,
'data':data}), 'data':data}),
namespace='/isard-sso-admin/sio', namespace='/sio',
room='admin') room='admin')
sleep(0.1) sleep(0.001)
def reload(self):
app.socketio.emit('reload',
json.dumps({}),
namespace='/sio',
room='admin')
sleep(0.0001)
def table(self,event,table,data={}):
# refresh, add, delete, update
app.socketio.emit('table_'+event,
json.dumps({'table':table,'data':data}),
namespace='/sio',
room='admin')
sleep(0.0001)

View File

@ -60,7 +60,7 @@ class KeycloakClient():
# self.add_role('superman') # self.add_role('superman')
# pprint(self.get_roles()) # pprint(self.get_roles())
## USERS ''' USERS '''
def get_user_id(self,username): def get_user_id(self,username):
self.connect() self.connect()
@ -71,7 +71,7 @@ class KeycloakClient():
return self.keycloak_admin.get_users({}) return self.keycloak_admin.get_users({})
def get_users_with_groups_and_roles(self): 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 q = """select u.id, u.username, u.email, u.first_name, u.last_name, u.realm_id, u.enabled, 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(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 ,json_agg(r.name) as role
from user_entity as u from user_entity as u
@ -82,7 +82,7 @@ class KeycloakClient():
left join keycloak_group as g_parent2 on g_parent.parent_group = g_parent2.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 user_role_mapping as rm on rm.user_id = u.id
left join keycloak_role as r on r.id = rm.role_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 group by u.id,u.username,u.email,u.first_name,u.last_name, u.realm_id, u.enabled, ua.value
order by u.username""" order by u.username"""
(headers,users)=self.keycloak_pg.select_with_headers(q) (headers,users)=self.keycloak_pg.select_with_headers(q)
@ -103,6 +103,7 @@ class KeycloakClient():
def getparent(self,group_id, data): def getparent(self,group_id, data):
# Recursively get full path from any group_id in the tree
path = "" path = ""
for item in data: for item in data:
if group_id == item[0]: if group_id == item[0]:
@ -111,11 +112,15 @@ class KeycloakClient():
return path return path
def get_group_path(self,group_id): def get_group_path(self,group_id):
# Get full path using getparent recursive func
# RETURNS: String with full path
q = """SELECT * FROM keycloak_group""" q = """SELECT * FROM keycloak_group"""
groups=self.keycloak_pg.select(q) groups=self.keycloak_pg.select(q)
return self.getparent(group_id,groups) return self.getparent(group_id,groups)
def get_user_groups_paths(self,user_id): def get_user_groups_paths(self,user_id):
# Get full paths for user grups
# RETURNS list of paths
q = """SELECT group_id FROM user_group_membership WHERE user_id = '%s'""" % (user_id) q = """SELECT group_id FROM user_group_membership WHERE user_id = '%s'""" % (user_id)
user_group_ids=self.keycloak_pg.select(q) user_group_ids=self.keycloak_pg.select(q)
@ -124,7 +129,7 @@ class KeycloakClient():
paths.append(self.get_group_path(g[0])) paths.append(self.get_group_path(g[0]))
return paths return paths
## Too slow ## Too slow. Used the direct postgres
# def get_users_with_groups_and_roles(self): # def get_users_with_groups_and_roles(self):
# self.connect() # self.connect()
# users=self.keycloak_admin.get_users({}) # users=self.keycloak_admin.get_users({})
@ -134,7 +139,7 @@ class KeycloakClient():
# return users # return users
def add_user(self,username,first,last,email,password,group=False,temporary=True): def add_user(self,username,first,last,email,password,group=False,temporary=True):
# Returns user id # RETURNS string with keycloak user id (the main id in this app)
self.connect() self.connect()
username=username.lower() username=username.lower()
try: try:
@ -159,12 +164,13 @@ class KeycloakClient():
self.keycloak_admin.group_user_add(uid,gid) self.keycloak_admin.group_user_add(uid,gid)
return uid return uid
def update_user_pwd(self,user_id='61092e24-cd67-4b50-baf9-b60e01f12bff',payload={},temporary=True): def update_user_pwd(self,user_id,password,temporary=True):
# Updates
payload={"credentials":[{"type":"password", payload={"credentials":[{"type":"password",
"value":'pepito', "value":password,
"temporary":temporary}]} "temporary":temporary}]}
self.connect() self.connect()
self.keycloak_admin.update_user( user_id, payload) return self.keycloak_admin.update_user( user_id, payload)
def remove_user_group(self,user_id,group_id): def remove_user_group(self,user_id,group_id):
self.connect() self.connect()
@ -219,7 +225,6 @@ class KeycloakClient():
def add_group(self,name,parent=None,skip_exists=False): def add_group(self,name,parent=None,skip_exists=False):
self.connect() self.connect()
print('parent_path: '+str(parent))
if parent is not None: parent=self.get_group_by_path(parent)['id'] if parent is not None: parent=self.get_group_by_path(parent)['id']
return self.keycloak_admin.create_group({"name":name}, parent=parent) return self.keycloak_admin.create_group({"name":name}, parent=parent)
@ -286,11 +291,11 @@ class KeycloakClient():
else: else:
thepath=parent_path+'/'+parts[i] thepath=parent_path+'/'+parts[i]
if thepath=='/': if thepath=='/':
print('Not adding the user '+username+' to any group as does not have any...') log.warning('Not adding the user '+username+' to any group as does not have any...')
continue continue
gid=self.get_group_by_path(path=thepath)['id'] gid=self.get_group_by_path(path=thepath)['id']
print('Adding '+username+' with uuid: '+uid+' to group '+g+' with uuid: '+gid) log.warning('Adding '+username+' with uuid: '+uid+' to group '+g+' with uuid: '+gid)
self.keycloak_admin.group_user_add(uid,gid) self.keycloak_admin.group_user_add(uid,gid)

View File

@ -88,15 +88,16 @@ class Moodle():
def get_users_with_groups_and_roles(self): def get_users_with_groups_and_roles(self):
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 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 from mdl_user as u
LEFT JOIN mdl_cohort_members AS hm on hm.id = u.id LEFT JOIN mdl_cohort_members AS hm on hm.userid = u.id
left join mdl_cohort AS h ON h.id = hm.cohortid 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_assignments AS ra ON ra.id = u.id
left join mdl_role as r on r.id = ra.roleid left join mdl_role as r on r.id = ra.roleid
where u.deleted = 0
group by u.id , username, first, last, email""" group by u.id , username, first, last, email"""
(headers,users)=self.moodle_pg.select_with_headers(q) (headers,users)=self.moodle_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] 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] list_dict_users = [dict(zip(headers, r)) for r in users_with_lists]
# pprint(list_dict_users) pprint(list_dict_users)
return list_dict_users return list_dict_users
## NOT USED. Too slow ## NOT USED. Too slow

View File

@ -1,19 +0,0 @@
#Add this app service
registres mdl_external_services
registres mdl_external_services_functions
registres mdl_external_services_users
# SAML
mdl_auth_saml2_idps
/opt/digitaldemocratic/data/moodle/saml2# ls
0f635d0e0f3874fff8b581c132e6c7a7.idp.xml moodle.santantoni.duckdns.org.crt moodle.santantoni.duckdns.org.pem
echo -n xml | md5sum
0f635d0e0f3874fff8b581c132e6c7a7
SELECT * FROM "mdl_config" WHERE ("name" LIKE '%saml%' OR "value" LIKE '%saml%') LIMIT 50 (0.001 s) Edita
Modify id name value
edita 3 auth email,saml2
privatekey_pass = mdl_config siteidentifier

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

View File

@ -20,7 +20,7 @@ $(document).ready(function() {
// console.log(ids) // console.log(ids)
$.ajax({ $.ajax({
type: "PUT", type: "PUT",
url:"/isard-sso-admin/external", url:"/api/external",
data: JSON.stringify(ids), data: JSON.stringify(ids),
success: function(data) success: function(data)
{ {
@ -58,7 +58,7 @@ $(document).ready(function() {
} }
$.ajax({ $.ajax({
type: "POST", type: "POST",
url:"/isard-sso-admin/external", url:"/api/external",
data: JSON.stringify(formdata), data: JSON.stringify(formdata),
success: function(data) success: function(data)
{ {
@ -111,7 +111,7 @@ $(document).ready(function() {
}).get().on('pnotify.confirm', function() { }).get().on('pnotify.confirm', function() {
$.ajax({ $.ajax({
type: "PUT", type: "PUT",
url:"/isard-sso-admin/external/roles", url:"/api/external/roles",
data: JSON.stringify({'ids':ids,'action':action}), data: JSON.stringify({'ids':ids,'action':action}),
success: function(data) success: function(data)
{ {
@ -132,7 +132,7 @@ $(document).ready(function() {
//DataTable Main renderer //DataTable Main renderer
var users_table = $('#users').DataTable({ var users_table = $('#users').DataTable({
"ajax": { "ajax": {
"url": "/isard-sso-admin/external_users_list", "url": "/api/external/users",
"dataSrc": "" "dataSrc": ""
}, },
"language": { "language": {
@ -171,7 +171,7 @@ $(document).ready(function() {
var groups_table = $('#groups').DataTable({ var groups_table = $('#groups').DataTable({
"ajax": { "ajax": {
"url": "/isard-sso-admin/external_groups_list", "url": "/api/external/groups",
"dataSrc": "" "dataSrc": ""
}, },
"language": { "language": {

View File

@ -8,7 +8,7 @@ $(document).ready(function() {
$('.btn-global-resync').on('click', function () { $('.btn-global-resync').on('click', function () {
$.ajax({ $.ajax({
type: "GET", type: "GET",
url:"/isard-sso-admin/resync", url:"api/resync",
success: function(data) success: function(data)
{ {
table.ajax.reload(); table.ajax.reload();
@ -39,7 +39,7 @@ $(document).ready(function() {
console.log(formdata) console.log(formdata)
// $.ajax({ // $.ajax({
// type: "POST", // type: "POST",
// "url": "/isard-sso-admin/groups_list", // "url": "/groups_list",
// success: function(data) // success: function(data)
// { // {
// console.log('SUCCESS') // console.log('SUCCESS')
@ -55,7 +55,7 @@ $(document).ready(function() {
$('.btn-delete_keycloak').on('click', function () { $('.btn-delete_keycloak').on('click', function () {
$.ajax({ $.ajax({
type: "DELETE", type: "DELETE",
url:"/isard-sso-admin/groups/keycloak", url:"/api/groups/keycloak",
success: function(data) success: function(data)
{ {
console.log('SUCCESS') console.log('SUCCESS')
@ -73,7 +73,7 @@ $(document).ready(function() {
//DataTable Main renderer //DataTable Main renderer
var table = $('#groups').DataTable({ var table = $('#groups').DataTable({
"ajax": { "ajax": {
"url": "/isard-sso-admin/groups_list", "url": "/api/groups",
"dataSrc": "" "dataSrc": ""
}, },
"language": { "language": {

View File

@ -8,7 +8,7 @@ $(document).ready(function() {
$('.btn-global-resync').on('click', function () { $('.btn-global-resync').on('click', function () {
$.ajax({ $.ajax({
type: "GET", type: "GET",
url:"/isard-sso-admin/resync", url:"/api/resync",
success: function(data) success: function(data)
{ {
table.ajax.reload(); table.ajax.reload();
@ -35,7 +35,7 @@ $(document).ready(function() {
//DataTable Main renderer //DataTable Main renderer
var table = $('#roles').DataTable({ var table = $('#roles').DataTable({
"ajax": { "ajax": {
"url": "/isard-sso-admin/roles_list", "url": "/api/roles",
"dataSrc": "" "dataSrc": ""
}, },
"language": { "language": {

View File

@ -1,8 +1,8 @@
notice={} notice={}
$lost=0; $lost=0;
socket = io.connect(location.protocol+'//' + document.domain +'/isard-sso-admin/sio'); socket = io.connect(location.protocol+'//' + document.domain +'/sio');
console.log(location.protocol+'//' + document.domain +'/isard-sso-admin/sio') console.log(location.protocol+'//' + document.domain +'/sio')
socket.on('connect', function() { socket.on('connect', function() {
if($lost){location.reload();} if($lost){location.reload();}
console.log('Listening status socket'); console.log('Listening status socket');
@ -32,6 +32,13 @@ socket.on('notify-destroy', function(data) {
socket.on('notify-update', function(data) { socket.on('notify-update', function(data) {
var data = JSON.parse(data); var data = JSON.parse(data);
if(!( data.id in notice)){
notice[data.id] = new PNotify({
title: data.title,
text: data.text,
hide: false
});
}
// console.log(data.text) // console.log(data.text)
notice[data.id].update({ notice[data.id].update({
text: data.text text: data.text

View File

@ -0,0 +1,443 @@
$(document).on('shown.bs.modal', '#modalAddDesktop', function () {
modal_add_desktops.columns.adjust().draw();
});
$(document).ready(function() {
$.ajax({
type: "GET",
"url": "/api/groups",
success: function(data)
{
data.forEach(element => {
var groupOrigins = [];
['keycloak', 'moodle', 'nextcloud'].forEach(o => {
if (element[o]) {
groupOrigins.push(o)
}
})
$(".groups-select").append(
'<option value=' + element.path + '>' + element.name + ' (' + groupOrigins.join(',') + ') </option>'
)
});
$('.groups-select').select2();
},
error: function(data)
{
alert('Something went wrong on our side...')
}
});
$.ajax({
type: "GET",
"url": "/api/roles",
success: function(data)
{
console.log('ROLES')
console.log(data)
data.forEach(element => {
$(".role-moodle-select, .role-nextcloud-select, .role-keycloak-select").append(
'<option value=' + element.id + '>' + element.name + '</option>'
)
})
},
error: function(data)
{
alert('Something went wrong on our side...')
}
});
$('.btn-global-resync').on('click', function () {
$.ajax({
type: "GET",
url:"/api/resync",
success: function(data)
{
console.log('Reloaded')
table.ajax.reload();
// $("#modalImport").modal('hide');
// users_table.ajax.reload();
// groups_table.ajax.reload();
},
error: function(data)
{
alert('Something went wrong on our side...')
}
});
});
// Open new user modal
$('.btn-new-user').on('click', function () {
$('#modalAddUser').modal({
backdrop: 'static',
keyboard: false
}).modal('show');
});
// Send new user form
$('#modalAddUser #send').on('click', function () {
var form = $('#modalAddUserForm');
formdata = form.serializeObject()
console.log('NEW USER')
console.log(formdata)
// $.ajax({
// type: "POST",
// "url": "/groups_list",
// success: function(data)
// {
// console.log('SUCCESS')
// // $("#modalAddUser").modal('hide');
// },
// error: function(data)
// {
// alert('Something went wrong on our side...')
// }
// });
});
$('.btn-delete_keycloak').on('click', function () {
$.ajax({
type: "DELETE",
url:"/api/users/keycloak",
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-delete_nextcloud').on('click', function () {
$.ajax({
type: "DELETE",
url:"/api/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...')
}
});
});
$('.btn-delete_moodle').on('click', function () {
$.ajax({
type: "DELETE",
url:"/api/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_moodle').on('click', function () {
$.ajax({
type: "POST",
url:"/api/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:"/api/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": {
"url": "/api/users",
"dataSrc": ""
},
"language": {
"loadingRecords": '<i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i><span class="sr-only">Loading...</span>',
"emptyTable": "<h1>You don't have any user created yet.</h1><br><h2>Create one using the +Add new button on top right of this page.</h2>"
},
"rowId": "id",
"deferRender": true,
"columns": [
{
"className": 'details-control',
"orderable": false,
"data": null,
"width": "10px",
"defaultContent": '<button class="btn btn-xs btn-info" type="button" data-placement="top" ><i class="fa fa-plus"></i></button>'
},
{ "data": "id", "width": "10px" },
{ "data": "username", "width": "10px"},
{ "data": "first", "width": "10px"},
{ "data": "last", "width": "10px"},
{ "data": "email", "width": "10px"},
{ "data": "keycloak", "width": "10px" },
{ "data": "keycloak_groups", "width": "10px" },
{ "data": "roles", "width": "10px" },
{ "data": "moodle", "width": "10px" },
{ "data": "moodle_groups", "width": "10px" },
{ "data": "nextcloud", "width": "10px" },
{ "data": "nextcloud_groups", "width": "10px" },
],
"order": [[4, 'asc']],
"columnDefs": [ {
"targets": 1,
"render": function ( data, type, full, meta ) {
return '<object data="/static/img/missing.jpg" type="image/jpg" width="25" height="25"><img src="/avatar/'+full.id+'" title="'+full.id+'" width="25" height="25"></object>'
}},
{
"targets": 6,
"render": function ( data, type, full, meta ) {
if(full.keycloak){
return '<i class="fa fa-check" style="color:lightgreen"></i>'
}else{
return '<i class="fa fa-close" style="color:darkred"></i>'
};
}},
{
"targets": 7,
"render": function ( data, type, full, meta ) {
return "<li>" + full.keycloak_groups.join("</li><li>") + "</li>"
}},
{
"targets": 9,
"render": function ( data, type, full, meta ) {
if(full.moodle){
return '<i class="fa fa-check" style="color:lightgreen"></i>'
}else{
return '<i class="fa fa-close" style="color:darkred"></i>'
};
}},
{
"targets": 10,
"render": function ( data, type, full, meta ) {
return "<li>" + full.moodle_groups.join("</li><li>") + "</li>"
}},
{
"targets": 11,
"render": function ( data, type, full, meta ) {
if(full.nextcloud){
return '<i class="fa fa-check" style="color:lightgreen"></i>'
}else{
return '<i class="fa fa-close" style="color:darkred"></i>'
};
}},
{
"targets": 12,
"render": function ( data, type, full, meta ) {
return "<li>" + full.nextcloud_groups.join("</li><li>") + "</li>"
}},
]
} );
$template = $(".template-detail-users");
$('#users').find('tbody').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var row = table.row( tr );
if ( row.child.isShown() ) {
// This row is already open - close it
row.child.hide();
tr.removeClass('shown');
}
else {
// Close other rows
if ( table.row( '.shown' ).length ) {
$('.details-control', table.row( '.shown' ).node()).click();
}
// Open this row
row.child( addUserDetailPannel(row.data()) ).show();
tr.addClass('shown');
actionsUserDetail()
}
} );
function addUserDetailPannel ( d ) {
$newPanel = $template.clone();
$newPanel.html(function(i, oldHtml){
return oldHtml.replace(/d.id/g, d.id).replace(/d.username/g, d.username);
});
return $newPanel
}
function actionsUserDetail(){
$('.btn-passwd').on('click', function () {
var closest=$(this).closest("div").parent();
var pk=closest.attr("data-pk");
$("#modalPasswdUserForm")[0].reset();
$('#modalPasswdUser').modal({
backdrop: 'static',
keyboard: false
}).modal('show');
$('#modalPasswdUserForm #id').val(pk);
});
$("#modalPasswdUser #send").on('click', function(e){
var form = $('#modalPasswdUserForm');
form.parsley().validate();
if (form.parsley().isValid()){
data=$('#modalPasswdUserForm').serializeObject();
data['id']=$('#modalPasswdUserForm #id').val();
new PNotify({
title: 'Confirmation Needed',
text: "Are you sure you want to update password for the user "+ username+"?",
hide: false,
opacity: 0.9,
confirm: {
confirm: true
},
buttons: {
closer: false,
sticker: false
},
history: {
history: false
},
addclass: 'pnotify-center'
}).get().on('pnotify.confirm', function() {
console.log('Updating user password...')
$.ajax({
type: "PUT",
url:"/api/user" + id,
success: function(data)
{
$(div_id + ' #id').val(data.id);
$(div_id + ' #username').val(data.username);
$(div_id + ' #email').val(data.email);
$(div_id + ' #firstname').val(data.firstname);
$(div_id + ' #lastname').val(data.lastname);
$(div_id + ' .groups-select').val(data.groups);
$(div_id + ' .role-moodle-select').val(data.roles);
$(div_id + ' .role-nextcloud-select').val(data.roles);
$(div_id + ' .role-keycloak-select').val(data.roles);
$('.groups-select, .role-moodle-select, .role-nextcloud-select, .role-keycloak-select').trigger('change');
}
});
}).on('pnotify.cancel', function() {
});
}
});
$('.btn-edit').on('click', function () {
var closest=$(this).closest("div").parent();
var pk=closest.attr("data-pk");
$("#modalEditUserForm")[0].reset();
$('#modalEditUser').modal({
backdrop: 'static',
keyboard: false
}).modal('show');
setUserDefault('#modalEditUser', pk);
$('#modalEdit').parsley();
});
$("#modalEditUser #send").on('click', function(e){
var form = $('#modalEditUserForm');
form.parsley().validate();
if (form.parsley().isValid()){
data=$('#modalEditUserForm').serializeObject();
data['id']=$('#modalEditUserForm #id').val();
console.log('Editing user...')
console.log(data)
}
});
$('.btn-delete').on('click', function () {
var closest=$(this).closest("div").parent();
var pk=closest.attr("data-pk");
var username=closest.attr("data-username");
console.log(username)
new PNotify({
title: 'Confirmation Needed',
text: "Are you sure you want to delete the user: "+ username+"?",
hide: false,
opacity: 0.9,
confirm: {
confirm: true
},
buttons: {
closer: false,
sticker: false
},
history: {
history: false
},
addclass: 'pnotify-center'
}).get().on('pnotify.confirm', function() {
console.log('Deleting user...')
}).on('pnotify.cancel', function() {
});
});
}
function setUserDefault(div_id, user_id) {
// $.ajax({
// type: "GET",
// url:"/api/user/" + id,
// success: function(data)
// {
// $(div_id + ' #id').val(data.id);
// $(div_id + ' #username').val(data.username);
// $(div_id + ' #email').val(data.email);
// $(div_id + ' #firstname').val(data.firstname);
// $(div_id + ' #lastname').val(data.lastname);
// $(div_id + ' .groups-select').val(data.groups);
// $(div_id + ' .role-moodle-select').val(data.roles);
// $(div_id + ' .role-nextcloud-select').val(data.roles);
// $(div_id + ' .role-keycloak-select').val(data.roles);
// $('.groups-select, .role-moodle-select, .role-nextcloud-select, .role-keycloak-select').trigger('change');
// }
// });
// MOCK
$(div_id + ' #id').val('b57c8d3f-ee08-4a1d-9873-f40c082b9c69');
$(div_id + ' #user-avatar').attr('src', '/static/img/usera.jpg');
$(div_id + ' #username').val('yedcaqwvt');
$(div_id + ' #email').val('yedcaqwvt@institutmariaespinalt.cat');
$(div_id + ' #firstname').val('Ymisno');
$(div_id + ' #lastname').val('Edcaqwvt tavnuoes');
$(div_id + ' .groups-select').val(['student', 'manager']);
$(div_id + ' .role-moodle-select').val('51cc1a95-94b7-48eb-aebb-1eba6745e09f');
$(div_id + ' .role-nextcloud-select').val('1e21ec95-b8c7-43b8-baad-1a31ad33f388');
$(div_id + ' .role-keycloak-select').val('13da53d5-c50b-42d9-8fbf-84f2ed7cbf9e');
$('.groups-select, .role-moodle-select, .role-nextcloud-select, .role-keycloak-select').trigger('change');
}
});

View File

@ -7,18 +7,18 @@ $(document).ready(function() {
$.ajax({ $.ajax({
type: "GET", type: "GET",
"url": "/isard-sso-admin/groups_list", "url": "/api/groups",
success: function(data) success: function(data)
{ {
data.forEach(element => { data.forEach(element => {
var groupOrigins = []; var groupOrigins = [];
['keycloak', 'moodle', 'nextcloud'].forEach(o => { ['keycloak'].forEach(o => {
if (element[o]) { if (element[o]) {
groupOrigins.push(o) groupOrigins.push(o)
} }
}) })
$(".groups-select").append( $(".groups-select").append(
'<option value=' + element.path + '>' + element.name + ' (' + groupOrigins.join(',') + ') </option>' '<option value=' + element.path + '>' + element.name + '</option>'
) )
}); });
$('.groups-select').select2(); $('.groups-select').select2();
@ -29,13 +29,35 @@ $(document).ready(function() {
} }
}); });
// $.ajax({
// type: "GET",
// "url": "/api/groups",
// success: function(data)
// {
// data.forEach(element => {
// var groupOrigins = [];
// ['keycloak', 'moodle', 'nextcloud'].forEach(o => {
// if (element[o]) {
// groupOrigins.push(o)
// }
// })
// $(".groups-select").append(
// '<option value=' + element.path + '>' + element.name + ' (' + groupOrigins.join(',') + ') </option>'
// )
// });
// $('.groups-select').select2();
// },
// error: function(data)
// {
// alert('Something went wrong on our side...')
// }
// });
$.ajax({ $.ajax({
type: "GET", type: "GET",
"url": "/isard-sso-admin/roles_list", "url": "/api/roles",
success: function(data) success: function(data)
{ {
console.log('ROLES')
console.log(data)
data.forEach(element => { data.forEach(element => {
$(".role-moodle-select, .role-nextcloud-select, .role-keycloak-select").append( $(".role-moodle-select, .role-nextcloud-select, .role-keycloak-select").append(
'<option value=' + element.id + '>' + element.name + '</option>' '<option value=' + element.id + '>' + element.name + '</option>'
@ -51,10 +73,9 @@ $(document).ready(function() {
$('.btn-global-resync').on('click', function () { $('.btn-global-resync').on('click', function () {
$.ajax({ $.ajax({
type: "GET", type: "GET",
url:"/isard-sso-admin/resync", url:"/api/resync",
success: function(data) success: function(data)
{ {
console.log('Reloaded')
table.ajax.reload(); table.ajax.reload();
// $("#modalImport").modal('hide'); // $("#modalImport").modal('hide');
// users_table.ajax.reload(); // users_table.ajax.reload();
@ -83,7 +104,7 @@ $(document).ready(function() {
console.log(formdata) console.log(formdata)
// $.ajax({ // $.ajax({
// type: "POST", // type: "POST",
// "url": "/isard-sso-admin/groups_list", // "url": "/groups_list",
// success: function(data) // success: function(data)
// { // {
// console.log('SUCCESS') // console.log('SUCCESS')
@ -99,7 +120,7 @@ $(document).ready(function() {
$('.btn-delete_keycloak').on('click', function () { $('.btn-delete_keycloak').on('click', function () {
$.ajax({ $.ajax({
type: "DELETE", type: "DELETE",
url:"/isard-sso-admin/users/keycloak", url:"/api/users/keycloak",
success: function(data) success: function(data)
{ {
console.log('SUCCESS') console.log('SUCCESS')
@ -117,7 +138,7 @@ $(document).ready(function() {
$('.btn-delete_nextcloud').on('click', function () { $('.btn-delete_nextcloud').on('click', function () {
$.ajax({ $.ajax({
type: "DELETE", type: "DELETE",
url:"/isard-sso-admin/users/nextcloud", url:"/api/users/nextcloud",
success: function(data) success: function(data)
{ {
console.log('SUCCESS') console.log('SUCCESS')
@ -135,7 +156,7 @@ $(document).ready(function() {
$('.btn-delete_moodle').on('click', function () { $('.btn-delete_moodle').on('click', function () {
$.ajax({ $.ajax({
type: "DELETE", type: "DELETE",
url:"/isard-sso-admin/users/moodle", url:"/api/users/moodle",
success: function(data) success: function(data)
{ {
console.log('SUCCESS') console.log('SUCCESS')
@ -152,7 +173,7 @@ $(document).ready(function() {
$('.btn-sync_to_moodle').on('click', function () { $('.btn-sync_to_moodle').on('click', function () {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url:"/isard-sso-admin/users/moodle", url:"/api/users/moodle",
success: function(data) success: function(data)
{ {
console.log('SUCCESS') console.log('SUCCESS')
@ -170,7 +191,7 @@ $(document).ready(function() {
$('.btn-sync_to_nextcloud').on('click', function () { $('.btn-sync_to_nextcloud').on('click', function () {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url:"/isard-sso-admin/users/nextcloud", url:"/api/users/nextcloud",
success: function(data) success: function(data)
{ {
console.log('SUCCESS') console.log('SUCCESS')
@ -188,7 +209,7 @@ $(document).ready(function() {
//DataTable Main renderer //DataTable Main renderer
var table = $('#users').DataTable({ var table = $('#users').DataTable({
"ajax": { "ajax": {
"url": "/isard-sso-admin/users_list", "url": "/api/users",
"dataSrc": "" "dataSrc": ""
}, },
"language": { "language": {
@ -198,73 +219,57 @@ $(document).ready(function() {
"rowId": "id", "rowId": "id",
"deferRender": true, "deferRender": true,
"columns": [ "columns": [
{"data": null, "defaultContent":'',"width": "1px"},
// {
// "className": 'details-control',
// "orderable": false,
// "data": null,
// "width": "10px",
// "defaultContent": '<button class="btn btn-xs btn-info" type="button" data-placement="top" ><i class="fa fa-plus"></i></button>'
// },
{ "data": "id", "width": "10px" },
{ "data": "enabled", "width": "10px" },
{ "data": "username", "width": "10px"},
{ {
"className": 'details-control', "className": 'actions-control',
"orderable": false, "orderable": false,
"data": null, "data": null,
"width": "10px", "width": "80px",
"defaultContent": '<button class="btn btn-xs btn-info" type="button" data-placement="top" ><i class="fa fa-plus"></i></button>' "defaultContent": '<button id="btn-edit" class="btn btn-xs" type="button" data-placement="top" ><i class="fa fa-pencil" style="color:darkblue"></i></button> \
<button id="btn-delete" class="btn btn-xs" type="button" data-placement="top" ><i class="fa fa-times" style="color:darkred"></i></button> \
<button id="btn-password" class="btn btn-xs" type="button" data-placement="top" ><i class="fa fa-lock" style="color:orange"></i></button>'
}, },
{ "data": "id", "width": "10px" },
{ "data": "username", "width": "10px"},
{ "data": "first", "width": "10px"}, { "data": "first", "width": "10px"},
{ "data": "last", "width": "10px"}, { "data": "last", "width": "200px"},
{ "data": "email", "width": "10px"}, { "data": "email", "width": "10px"},
{ "data": "keycloak", "width": "10px" },
{ "data": "keycloak_groups", "width": "10px" }, { "data": "keycloak_groups", "width": "10px" },
{ "data": "roles", "width": "10px" }, { "data": "roles", "width": "10px" },
{ "data": "moodle", "width": "10px" },
{ "data": "moodle_groups", "width": "10px" },
{ "data": "nextcloud", "width": "10px" },
{ "data": "nextcloud_groups", "width": "10px" },
], ],
"order": [[4, 'asc']], "order": [[6, 'asc']],
"columnDefs": [ { "columnDefs": [ {
"targets": 1, "targets": 1,
"render": function ( data, type, full, meta ) { "render": function ( data, type, full, meta ) {
return '<img src="/isard-sso-admin/avatars/'+full.id+'" title="'+full.id+'" width="25" height="25"></object>' // return '<object data="/static/img/missing.jpg" type="image/jpeg" width="25" height="25"><img src="/avatar/'+full.id+'" title="'+full.id+'" width="25" height="25"></object>'
return '<img src="/avatar/'+full.id+'" title="'+full.id+'" width="25" height="25">'
}}, }},
{ {
"targets": 6, "targets": 2,
"render": function ( data, type, full, meta ) { "render": function ( data, type, full, meta ) {
if(full.keycloak){ if(full.enabled){
return '<i class="fa fa-check" style="color:lightgreen"></i>' return '<i class="fa fa-check" style="color:lightgreen"></i>'
}else{ }else{
return '<i class="fa fa-close" style="color:darkred"></i>' return '<i class="fa fa-close" style="color:darkred"></i>'
}; };
}}, }},
{ {
"targets": 7, "targets": 3,
"render": function ( data, type, full, meta ) {
return '<b>'+full.username+'</b>'
}},
{
"targets": 8,
"render": function ( data, type, full, meta ) { "render": function ( data, type, full, meta ) {
return "<li>" + full.keycloak_groups.join("</li><li>") + "</li>" return "<li>" + full.keycloak_groups.join("</li><li>") + "</li>"
}},
{
"targets": 9,
"render": function ( data, type, full, meta ) {
if(full.moodle){
return '<i class="fa fa-check" style="color:lightgreen"></i>'
}else{
return '<i class="fa fa-close" style="color:darkred"></i>'
};
}},
{
"targets": 10,
"render": function ( data, type, full, meta ) {
return "<li>" + full.moodle_groups.join("</li><li>") + "</li>"
}},
{
"targets": 11,
"render": function ( data, type, full, meta ) {
if(full.nextcloud){
return '<i class="fa fa-check" style="color:lightgreen"></i>'
}else{
return '<i class="fa fa-close" style="color:darkred"></i>'
};
}},
{
"targets": 12,
"render": function ( data, type, full, meta ) {
return "<li>" + full.nextcloud_groups.join("</li><li>") + "</li>"
}}, }},
] ]
} ); } );
@ -292,69 +297,26 @@ $(document).ready(function() {
} }
} ); } );
function addUserDetailPannel ( d ) { $('#users').find(' tbody').on( 'click', 'button', function () {
$newPanel = $template.clone(); var data = table.row( $(this).parents('tr') ).data();
$newPanel.html(function(i, oldHtml){ // var closest=$(this).closest("div").parent();
return oldHtml.replace(/d.id/g, d.id).replace(/d.username/g, d.username); // var pk=closest.attr("data-pk");
}); // console.log(pk)
return $newPanel switch($(this).attr('id')){
} case 'btn-edit':
function actionsUserDetail(){
$('.btn-passwd').on('click', function () {
var closest=$(this).closest("div").parent();
var pk=closest.attr("data-pk");
$("#modalPasswdUserForm")[0].reset();
$('#modalPasswdUser').modal({
backdrop: 'static',
keyboard: false
}).modal('show');
$('#modalPasswdUserForm #id').val(pk);
});
$("#modalPasswdUser #send").on('click', function(e){
var form = $('#modalPasswdUserForm');
form.parsley().validate();
if (form.parsley().isValid()){
data=$('#modalPasswdUserForm').serializeObject();
data['id']=$('#modalPasswdUserForm #id').val();
console.log('Editing user password...')
console.log(data)
}
});
$('.btn-edit').on('click', function () {
var closest=$(this).closest("div").parent();
var pk=closest.attr("data-pk");
$("#modalEditUserForm")[0].reset(); $("#modalEditUserForm")[0].reset();
$('#modalEditUser').modal({ $('#modalEditUser').modal({
backdrop: 'static', backdrop: 'static',
keyboard: false keyboard: false
}).modal('show'); }).modal('show');
setUserDefault('#modalEditUser', pk); $('#modalEditUser #user-avatar').attr("src","/avatar/"+data.id)
setUserDefault('#modalEditUser', data.id);
$('#modalEdit').parsley(); $('#modalEdit').parsley();
}); break;
case 'btn-delete':
$("#modalEditUser #send").on('click', function(e){
var form = $('#modalEditUserForm');
form.parsley().validate();
if (form.parsley().isValid()){
data=$('#modalEditUserForm').serializeObject();
data['id']=$('#modalEditUserForm #id').val();
console.log('Editing user...')
console.log(data)
}
});
$('.btn-delete').on('click', function () {
var closest=$(this).closest("div").parent();
var pk=closest.attr("data-pk");
var username=closest.attr("data-username");
console.log(username)
new PNotify({ new PNotify({
title: 'Confirmation Needed', title: 'Confirmation Needed',
text: "Are you sure you want to delete the user: "+ username+"?", text: "Are you sure you want to delete user: "+data['username']+"?",
hide: false, hide: false,
opacity: 0.9, opacity: 0.9,
confirm: { confirm: {
@ -369,40 +331,187 @@ $(document).ready(function() {
}, },
addclass: 'pnotify-center' addclass: 'pnotify-center'
}).get().on('pnotify.confirm', function() { }).get().on('pnotify.confirm', function() {
console.log('Deleting user...') $.ajax({
type: "DELETE",
url:"/api/user/"+data.id,
success: function(data)
{
table.ajax.reload();
},
error: function(data)
{
alert('Something went wrong on our side...')
}
});
}).on('pnotify.cancel', function() { }).on('pnotify.cancel', function() {
}); });
break;
case 'btn-password':
$("#modalPasswdUserForm")[0].reset();
$('#modalPasswdUser').modal({
backdrop: 'static',
keyboard: false
}).modal('show');
$('#modalPasswdUserForm #id').val(data.id);
$.ajax({
type: "GET",
url:"/api/user_password",
success: function(data)
{
$('#modalPasswdUserForm #password').val(data);
},
error: function(data)
{
alert('Something went wrong on our side...')
}
}); });
break;
}
});
$("#modalPasswdUser #send").on('click', function(e){
var form = $('#modalPasswdUserForm');
form.parsley().validate();
if (form.parsley().isValid()){
formdata=$('#modalPasswdUserForm').serializeObject();
id=$('#modalPasswdUserForm #id').val();
$.ajax({
type: "PUT",
url:"/api/user//+" + id,
data: JSON.stringify(formdata),
success: function(data)
{
$("#modalPasswdUser").modal('hide');
table.ajax.reload();
// groups_table.ajax.reload();
},
error: function(data)
{
alert('Something went wrong on our side...')
}
// statusCode: {
// 404: function(data) {
// // {'error': 'description}. Not able to get responseJSON from received object
// alert('User not exists in system!')
// },
// 200: function() {
// console.log("Success");
// }
// },
// error: function(data)
// {
// alert('Something went wrong on our side...')
// }
});
}
});
function addUserDetailPannel ( d ) {
$newPanel = $template.clone();
$newPanel.html(function(i, oldHtml){
return oldHtml.replace(/d.id/g, d.id).replace(/d.username/g, d.username);
});
return $newPanel
}
function actionsUserDetail(){
// $('.btn-passwd').on('click', function () {
// var closest=$(this).closest("div").parent();
// var pk=closest.attr("data-pk");
// $("#modalPasswdUserForm")[0].reset();
// $('#modalPasswdUser').modal({
// backdrop: 'static',
// keyboard: false
// }).modal('show');
// $('#modalPasswdUserForm #id').val(pk);
// });
// $('.btn-edit').on('click', function () {
// var closest=$(this).closest("div").parent();
// var pk=closest.attr("data-pk");
// $("#modalEditUserForm")[0].reset();
// $('#modalEditUser').modal({
// backdrop: 'static',
// keyboard: false
// }).modal('show');
// setUserDefault('#modalEditUser', pk);
// $('#modalEdit').parsley();
// });
$("#modalEditUser #send").on('click', function(e){
var form = $('#modalEditUserForm');
form.parsley().validate();
if (form.parsley().isValid()){
data=$('#modalEditUserForm').serializeObject();
data['id']=$('#modalEditUserForm #id').val();
console.log('Editing user...')
console.log(data)
}
});
// $('.btn-delete').on('click', function () {
// var closest=$(this).closest("div").parent();
// var pk=closest.attr("data-pk");
// var username=closest.attr("data-username");
// console.log(username)
// new PNotify({
// title: 'Confirmation Needed',
// text: "Are you sure you want to delete the user: "+ username+"?",
// hide: false,
// opacity: 0.9,
// confirm: {
// confirm: true
// },
// buttons: {
// closer: false,
// sticker: false
// },
// history: {
// history: false
// },
// addclass: 'pnotify-center'
// }).get().on('pnotify.confirm', function() {
// console.log('Deleting user...')
// }).on('pnotify.cancel', function() {
// });
// });
} }
function setUserDefault(div_id, user_id) { function setUserDefault(div_id, user_id) {
// $.ajax({ $.ajax({
// type: "GET", type: "GET",
// url:"/isard-sso-admin/user/" + id, url:"/api/user/" + user_id,
// success: function(data) success: function(data)
// { {
// $(div_id + ' #id').val(data.id); $(div_id + ' #id').val(data.id);
// $(div_id + ' #username').val(data.username); $(div_id + ' #username').val(data.username);
// $(div_id + ' #email').val(data.email); $(div_id + ' #email').val(data.email);
// $(div_id + ' #firstname').val(data.firstname); $(div_id + ' #firstname').val(data.first);
// $(div_id + ' #lastname').val(data.lastname); $(div_id + ' #lastname').val(data.last);
// $(div_id + ' .groups-select').val(data.groups); console.log(data.keycloak_groups)
// $(div_id + ' .role-moodle-select').val(data.roles); $(div_id + ' .groups-select').val(data.keycloak_groups);
// $(div_id + ' .role-moodle-select').val(data.keycloak_roles);
// $(div_id + ' .role-nextcloud-select').val(data.roles); // $(div_id + ' .role-nextcloud-select').val(data.roles);
// $(div_id + ' .role-keycloak-select').val(data.roles); $(div_id + ' .role-keycloak-select').val(data.keycloak_roles);
$('.groups-select, .role-keycloak-select').trigger('change');
// $('.groups-select, .role-moodle-select, .role-nextcloud-select, .role-keycloak-select').trigger('change');
}
});
// MOCK
// $(div_id + ' #id').val('b57c8d3f-ee08-4a1d-9873-f40c082b9c69');
// $(div_id + ' #user-avatar').attr('src', 'static/img/usera.jpg');
// $(div_id + ' #username').val('yedcaqwvt');
// $(div_id + ' #email').val('yedcaqwvt@institutmariaespinalt.cat');
// $(div_id + ' #firstname').val('Ymisno');
// $(div_id + ' #lastname').val('Edcaqwvt tavnuoes');
// $(div_id + ' .groups-select').val(['student', 'manager']);
// $(div_id + ' .role-moodle-select').val('51cc1a95-94b7-48eb-aebb-1eba6745e09f');
// $(div_id + ' .role-nextcloud-select').val('1e21ec95-b8c7-43b8-baad-1a31ad33f388');
// $(div_id + ' .role-keycloak-select').val('13da53d5-c50b-42d9-8fbf-84f2ed7cbf9e');
// $('.groups-select, .role-moodle-select, .role-nextcloud-select, .role-keycloak-select').trigger('change'); // $('.groups-select, .role-moodle-select, .role-nextcloud-select, .role-keycloak-select').trigger('change');
// }
// });
// MOCK
$(div_id + ' #id').val('b57c8d3f-ee08-4a1d-9873-f40c082b9c69');
$(div_id + ' #user-avatar').attr('src', 'static/img/usera.jpg');
$(div_id + ' #username').val('yedcaqwvt');
$(div_id + ' #email').val('yedcaqwvt@institutmariaespinalt.cat');
$(div_id + ' #firstname').val('Ymisno');
$(div_id + ' #lastname').val('Edcaqwvt tavnuoes');
$(div_id + ' .groups-select').val(['student', 'manager']);
$(div_id + ' .role-moodle-select').val('51cc1a95-94b7-48eb-aebb-1eba6745e09f');
$(div_id + ' .role-nextcloud-select').val('1e21ec95-b8c7-43b8-baad-1a31ad33f388');
$(div_id + ' .role-keycloak-select').val('13da53d5-c50b-42d9-8fbf-84f2ed7cbf9e');
$('.groups-select, .role-moodle-select, .role-nextcloud-select, .role-keycloak-select').trigger('change');
} }
}); });

View File

@ -14,27 +14,27 @@
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" /> <link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" /> <link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
<!-- Fancytree --> <!-- Fancytree -->
<link href="/isard-sso-admin/static/vendor/fancytree/dist/skin-win8/ui.fancytree.css" rel="stylesheet"> <link href="/static/vendor/fancytree/dist/skin-win8/ui.fancytree.css" rel="stylesheet">
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="/isard-sso-admin/vendors/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="/vendors/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome --> <!-- Font Awesome -->
<link href="/isard-sso-admin/vendors/font-awesome/css/font-awesome.min.css" rel="stylesheet"> <link href="/vendors/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<!-- ion.rangeSlider --> <!-- ion.rangeSlider -->
<link href="/isard-sso-admin/vendors/ion.rangeSlider/css/ion.rangeSlider.css" rel="stylesheet"> <link href="/vendors/ion.rangeSlider/css/ion.rangeSlider.css" rel="stylesheet">
<link href="/isard-sso-admin/vendors/ion.rangeSlider/css/ion.rangeSlider.skinFlat.css" rel="stylesheet"> <link href="/vendors/ion.rangeSlider/css/ion.rangeSlider.skinFlat.css" rel="stylesheet">
<!-- Datatables --> <!-- Datatables -->
<link href="/isard-sso-admin/vendors/datatables.net-bs/css/dataTables.bootstrap.min.css" rel="stylesheet"> <link href="/vendors/datatables.net-bs/css/dataTables.bootstrap.min.css" rel="stylesheet">
<!-- PNotify --> <!-- PNotify -->
<link href="/isard-sso-admin/vendors/pnotify/dist/pnotify.css" media="all" rel="stylesheet" type="text/css" /> <link href="/vendors/pnotify/dist/pnotify.css" media="all" rel="stylesheet" type="text/css" />
<link href="/isard-sso-admin/vendors/pnotify/dist/pnotify.buttons.css" media="all" rel="stylesheet" type="text/css" /> <link href="/vendors/pnotify/dist/pnotify.buttons.css" media="all" rel="stylesheet" type="text/css" />
<!-- iCheck --> <!-- iCheck -->
<link href="/isard-sso-admin/vendors/iCheck/skins/flat/green.css" rel="stylesheet"> <link href="/vendors/iCheck/skins/flat/green.css" rel="stylesheet">
<link href="/isard-sso-admin/vendors/select2/dist/css/select2.min.css" rel="stylesheet"> <link href="/vendors/select2/dist/css/select2.min.css" rel="stylesheet">
{% block css %}{% endblock %} {% block css %}{% endblock %}
<!-- Custom Theme Style --> <!-- Custom Theme Style -->
<link href="/isard-sso-admin/build/css/custom.css" rel="stylesheet"> <link href="/build/css/custom.css" rel="stylesheet">
<!-- Isard Style Sheet--> <!-- Isard Style Sheet-->
<link href="/isard-sso-admin/static/dd.css" rel="stylesheet"> <link href="/static/dd.css" rel="stylesheet">
</head> </head>
<body class="nav-md"> <body class="nav-md">
@ -64,44 +64,44 @@
<!-- jQuery --> <!-- jQuery -->
<script src="/isard-sso-admin/vendors/jquery/dist/jquery.js"></script> <script src="/vendors/jquery/dist/jquery.js"></script>
<!-- Bootstrap --> <!-- Bootstrap -->
<script src="/isard-sso-admin/vendors/bootstrap/dist/js/bootstrap.min.js"></script> <script src="/vendors/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- NProgress --> <!-- NProgress -->
<script src="/isard-sso-admin/vendors/nprogress/nprogress.js"></script> <script src="/vendors/nprogress/nprogress.js"></script>
<!-- Datatables --> <!-- Datatables -->
<script src="/isard-sso-admin/vendors/datatables.net/js/jquery.dataTables.min.js"></script> <script src="/vendors/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="/isard-sso-admin/vendors/datatables.net-bs/js/dataTables.bootstrap.min.js"></script> <script src="/vendors/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
<!-- Ion.RangeSlider --> <!-- Ion.RangeSlider -->
<script src="/isard-sso-admin/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script> <script src="/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script>
<!-- PNotify --> <!-- PNotify -->
<script type="text/javascript" src="/isard-sso-admin/vendors/pnotify/dist/pnotify.js"></script> <script type="text/javascript" src="/vendors/pnotify/dist/pnotify.js"></script>
<script type="text/javascript" src="/isard-sso-admin/vendors/pnotify/dist/pnotify.confirm.js"></script> <script type="text/javascript" src="/vendors/pnotify/dist/pnotify.confirm.js"></script>
<script type="text/javascript" src="/isard-sso-admin/vendors/pnotify/dist/pnotify.buttons.js"></script> <script type="text/javascript" src="/vendors/pnotify/dist/pnotify.buttons.js"></script>
<!-- validator --> <!-- validator -->
<script src="/isard-sso-admin/vendors/validator/validator.js"></script> <script src="/vendors/validator/validator.js"></script>
<!-- Parsley --> <!-- Parsley -->
<script src="/isard-sso-admin/vendors/parsleyjs/dist/parsley.min.js"></script> <script src="/vendors/parsleyjs/dist/parsley.min.js"></script>
<!-- moment --> <!-- moment -->
<script src="/isard-sso-admin/vendors/moment/min/moment.min.js"></script> <script src="/vendors/moment/min/moment.min.js"></script>
<!-- validator --> <!-- validator -->
<script src="/isard-sso-admin/vendors/iCheck/icheck.min.js"></script> <script src="/vendors/iCheck/icheck.min.js"></script>
<!-- bootstrap-progressbar --> <!-- bootstrap-progressbar -->
<script src="/isard-sso-admin/vendors/bootstrap-progressbar/bootstrap-progressbar.min.js"></script> <script src="/vendors/bootstrap-progressbar/bootstrap-progressbar.min.js"></script>
<!-- ECharts --> <!-- ECharts -->
<script src="/isard-sso-admin/vendors/echarts/dist/echarts.min.js"></script> <script src="/vendors/echarts/dist/echarts.min.js"></script>
<!-- Select2 --> <!-- Select2 -->
<script src="/isard-sso-admin/vendors/select2/dist/js/select2.full.min.js"></script> <script src="/vendors/select2/dist/js/select2.full.min.js"></script>
<!-- SocketIO --> <!-- SocketIO -->
<script src="/isard-sso-admin/static/vendor/socket.io-2.3.1.slim.js"></script> <script src="/static/vendor/socket.io-2.3.1.slim.js"></script>
<script src="/isard-sso-admin/static/js/status_socket.js"></script> <script src="/static/js/status_socket.js"></script>
<!-- isard initializers --> <!-- isard initializers -->
<script src="/isard-sso-admin/static/dd.js"></script> <script src="/static/dd.js"></script>
<!-- Requirements for fancy tree --> <!-- Requirements for fancy tree -->
<script src="/isard-sso-admin/static/vendor/fancytree/src/jquery-ui-dependencies/jquery-ui.min.js"></script> <script src="/static/vendor/fancytree/src/jquery-ui-dependencies/jquery-ui.min.js"></script>
<script src="/isard-sso-admin/static/vendor/fancytree/dist/jquery.fancytree.min.js"></script> <script src="/static/vendor/fancytree/dist/jquery.fancytree.min.js"></script>
<script src="/isard-sso-admin/static/vendor/fancytree/src/jquery.fancytree.table.js"></script> <script src="/static/vendor/fancytree/src/jquery.fancytree.table.js"></script>
<!-- flashed messages with pnotify --> <!-- flashed messages with pnotify -->
{% with messages = get_flashed_messages(with_categories=true) %} {% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %} {% if messages %}
@ -112,7 +112,7 @@
text: "{{ message }}", text: "{{ message }}",
hide: true, hide: true,
delay: 2000, delay: 2000,
//~ icon: 'fa fa-alert-sign', /~ icon: 'fa fa-alert-sign',
opacity: 1, opacity: 1,
type: "{{ category }}", type: "{{ category }}",
addclass: "pnotify-center" addclass: "pnotify-center"

View File

@ -1,6 +1,6 @@
<footer> <footer>
<div class="pull-right"> <div class="pull-right">
Digital Democratic - Administration | <a href="https://gitlab.com/digitaldemocratic/digitaldemocratic">gitlab</a> Digital Democratic - Administration | <a href="https:/gitlab.com/digitaldemocratic/digitaldemocratic">gitlab</a>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</footer> </footer>

View File

@ -7,12 +7,12 @@
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li class=""> <li class="">
<a href="javascript:" class="user-profile dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> <a href="javascript:" class="user-profile dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<img src="/isard-sso-admin/static/img/user.png" alt="..." > <img src="/static/img/user.png" alt="..." >
<span class=" fa fa-angle-down"></span> <span class=" fa fa-angle-down"></span>
</a> </a>
<ul class="dropdown-menu dropdown-usermenu pull-right"> <ul class="dropdown-menu dropdown-usermenu pull-right">
<li><a href="/isard-sso-admin/profile"><i class="fa fa-gear pull-right"></i> Profile</a></li> <li><a href="/profile"><i class="fa fa-gear pull-right"></i> Profile</a></li>
<li><a href="/isard-sso-admin/logout"><i class="fa fa-sign-out pull-right"></i> Log Out</a></li> <li><a href="/logout"><i class="fa fa-sign-out pull-right"></i> Log Out</a></li>
</ul> </ul>
</li> </li>

View File

@ -10,16 +10,16 @@
<title>Login | Digital Democratic</title> <title>Login | Digital Democratic</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="/isard-sso-admin/vendors/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="/vendors/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome --> <!-- Font Awesome -->
<link href="/isard-sso-admin/vendors/font-awesome/css/font-awesome.min.css" rel="stylesheet"> <link href="/vendors/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<!-- Animate.css --> <!-- Animate.css -->
<link href="/isard-sso-admin/vendors/animate.css/animate.min.css" rel="stylesheet"> <link href="/vendors/animate.css/animate.min.css" rel="stylesheet">
<!-- PNotify --> <!-- PNotify -->
<link href="/isard-sso-admin/vendors/pnotify/dist/pnotify.css" media="all" rel="stylesheet" type="text/css" /> <link href="/vendors/pnotify/dist/pnotify.css" media="all" rel="stylesheet" type="text/css" />
<link href="/isard-sso-admin/vendors/pnotify/dist/pnotify.buttons.css" media="all" rel="stylesheet" type="text/css" /> <link href="/vendors/pnotify/dist/pnotify.buttons.css" media="all" rel="stylesheet" type="text/css" />
<!-- Custom Theme Style --> <!-- Custom Theme Style -->
<link href="/isard-sso-admin/build/css/custom.min.css" rel="stylesheet"> <link href="/build/css/custom.min.css" rel="stylesheet">
</head> </head>
<body class="login"> <body class="login">
@ -47,7 +47,7 @@
<div> <div>
<h1><i class="fa fa-user"></i> Digital Democratic</h1> <h1><i class="fa fa-user"></i> Digital Democratic</h1>
<p>©2020 All Rights Reserved. <a href="https://gitlab.com/digitaldemocratic/digitaldemocratic/LICENSE" target="_blank">AGPLv3</a></p> <p>©2020 All Rights Reserved. <a href="https:/gitlab.com/digitaldemocratic/digitaldemocratic/LICENSE" target="_blank">AGPLv3</a></p>
</div> </div>
</div> </div>
</form> </form>
@ -58,11 +58,11 @@
</body> </body>
<!-- jQuery --> <!-- jQuery -->
<script src="/isard-sso-admin/vendors/jquery/dist/jquery.min.js"></script> <script src="/vendors/jquery/dist/jquery.min.js"></script>
<!-- PNotify --> <!-- PNotify -->
<script type="text/javascript" src="/isard-sso-admin/vendors/pnotify/dist/pnotify.js"></script> <script type="text/javascript" src="/vendors/pnotify/dist/pnotify.js"></script>
<script type="text/javascript" src="/isard-sso-admin/vendors/pnotify/dist/pnotify.confirm.js"></script> <script type="text/javascript" src="/vendors/pnotify/dist/pnotify.confirm.js"></script>
<script type="text/javascript" src="/isard-sso-admin/vendors/pnotify/dist/pnotify.buttons.js"></script> <script type="text/javascript" src="/vendors/pnotify/dist/pnotify.buttons.js"></script>
<script>PNotify.prototype.options.styling = "bootstrap3";</script> <script>PNotify.prototype.options.styling = "bootstrap3";</script>
{% with messages = get_flashed_messages(with_categories=true) %} {% with messages = get_flashed_messages(with_categories=true) %}
@ -73,7 +73,7 @@
title: "{{ nav }}", title: "{{ nav }}",
text: "{{ message }}", text: "{{ message }}",
hide: true, hide: true,
//~ icon: 'fa fa-alert-sign', /~ icon: 'fa fa-alert-sign',
opacity: 1, opacity: 1,
type: "error", type: "error",
addclass: "pnotify-center" addclass: "pnotify-center"
@ -83,5 +83,5 @@
{% endif %} {% endif %}
{% endwith %} {% endwith %}
<!-- Isard restful ajax calls --> <!-- Isard restful ajax calls -->
<script src="/isard-sso-admin/static/js/restful.js"></script> <script src="/static/js/restful.js"></script>
</html> </html>

View File

@ -10,14 +10,14 @@
<title>Page not found! | Digital Democratic</title> <title>Page not found! | Digital Democratic</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="../isard-sso-admin/vendors/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="../vendors/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome --> <!-- Font Awesome -->
<link href="../isard-sso-admin/vendors/font-awesome/css/font-awesome.min.css" rel="stylesheet"> <link href="../vendors/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<!-- NProgress --> <!-- NProgress -->
<link href="../isard-sso-admin/vendors/nprogress/nprogress.css" rel="stylesheet"> <link href="../vendors/nprogress/nprogress.css" rel="stylesheet">
<!-- Custom Theme Style --> <!-- Custom Theme Style -->
<link href="../isard-sso-admin/build/css/custom.min.css" rel="stylesheet"> <link href="../build/css/custom.min.css" rel="stylesheet">
</head> </head>
<body class="nav-md"> <body class="nav-md">
@ -29,8 +29,8 @@
<div class="text-center text-center"> <div class="text-center text-center">
<h1 class="error-number">404</h1> <h1 class="error-number">404</h1>
<h2>Sorry but we couldn't find this page</h2> <h2>Sorry but we couldn't find this page</h2>
<p>This page you are looking for does not exist <a href="https://gitlab.com/digitaldemocratic/digitaldemocratic">Report this?</a> <p>This page you are looking for does not exist <a href="https:/gitlab.com/digitaldemocratic/digitaldemocratic">Report this?</a>
<a href="/isard-sso-admin/login">Go back to login page</a> <a href="/login">Go back to login page</a>
</p> </p>
<!-- <!--
<div class="mid_center"> <div class="mid_center">
@ -55,15 +55,15 @@
</div> </div>
<!-- jQuery --> <!-- jQuery -->
<script src="../isard-sso-admin/vendors/jquery/dist/jquery.min.js"></script> <script src="../vendors/jquery/dist/jquery.min.js"></script>
<!-- Bootstrap --> <!-- Bootstrap -->
<script src="../isard-sso-admin/vendors/bootstrap/dist/js/bootstrap.min.js"></script> <script src="../vendors/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- FastClick --> <!-- FastClick -->
<script src="../isard-sso-admin/vendors/fastclick/lib/fastclick.js"></script> <script src="../vendors/fastclick/lib/fastclick.js"></script>
<!-- NProgress --> <!-- NProgress -->
<script src="../isard-sso-admin/vendors/nprogress/nprogress.js"></script> <script src="../vendors/nprogress/nprogress.js"></script>
<!-- Custom Theme Scripts --> <!-- Custom Theme Scripts -->
<script src="../isard-sso-admin/build/js/custom.min.js"></script> <script src="../build/js/custom.min.js"></script>
</body> </body>
</html> </html>

View File

@ -10,14 +10,14 @@
<title>Page not allowed! | Digital Democratic</title> <title>Page not allowed! | Digital Democratic</title>
<!-- Bootstrap --> <!-- Bootstrap -->
<link href="../isard-sso-admin/vendors/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="../vendors/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome --> <!-- Font Awesome -->
<link href="../isard-sso-admin/vendors/font-awesome/css/font-awesome.min.css" rel="stylesheet"> <link href="../vendors/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<!-- NProgress --> <!-- NProgress -->
<link href="../isard-sso-admin/vendors/nprogress/nprogress.css" rel="stylesheet"> <link href="../vendors/nprogress/nprogress.css" rel="stylesheet">
<!-- Custom Theme Style --> <!-- Custom Theme Style -->
<link href="../isard-sso-admin/build/css/custom.min.css" rel="stylesheet"> <link href="../build/css/custom.min.css" rel="stylesheet">
</head> </head>
<body class="nav-md"> <body class="nav-md">
@ -29,8 +29,8 @@
<div class="text-center"> <div class="text-center">
<h1 class="error-number">500</h1> <h1 class="error-number">500</h1>
<h2>Internal Server Error</h2> <h2>Internal Server Error</h2>
<p>We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing. <a href="https://gitlab.com/digitaldemocratic/digitaldemocratic">Report this?</a> <p>We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing. <a href="https:/gitlab.com/digitaldemocratic/digitaldemocratic">Report this?</a>
<a href="/isard-sso-admin/login">Go back to login page</a> <a href="/login">Go back to login page</a>
</p> </p>
<!-- <!--
<div class="mid_center"> <div class="mid_center">
@ -55,15 +55,15 @@
</div> </div>
<!-- jQuery --> <!-- jQuery -->
<script src="../isard-sso-admin/vendors/jquery/dist/jquery.min.js"></script> <script src="../vendors/jquery/dist/jquery.min.js"></script>
<!-- Bootstrap --> <!-- Bootstrap -->
<script src="../isard-sso-admin/vendors/bootstrap/dist/js/bootstrap.min.js"></script> <script src="../vendors/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- FastClick --> <!-- FastClick -->
<script src="../isard-sso-admin/vendors/fastclick/lib/fastclick.js"></script> <script src="../vendors/fastclick/lib/fastclick.js"></script>
<!-- NProgress --> <!-- NProgress -->
<script src="../isard-sso-admin/vendors/nprogress/nprogress.js"></script> <script src="../vendors/nprogress/nprogress.js"></script>
<!-- Custom Theme Scripts --> <!-- Custom Theme Scripts -->
<script src="../isard-sso-admin/build/js/custom.min.js"></script> <script src="../build/js/custom.min.js"></script>
</body> </body>
</html> </html>

View File

@ -6,19 +6,19 @@
<div class="col-md-12 col-sm-12 col-xs-12"> <div class="col-md-12 col-sm-12 col-xs-12">
<div class="x_panel"> <div class="x_panel">
<div class="container for-about text-center" style="margin-top: 15px;"> <div class="container for-about text-center" style="margin-top: 15px;">
<img src="/isard-sso-admin/static/img/dd.svg" width="250px" height="250px"> <img src="/static/img/dd.svg" width="250px" height="250px">
<h1>Digital Democratic</h1> <h1>Digital Democratic</h1>
<h1><small>Schools apps integrations</small></h1> <h1><small>Schools apps integrations</small></h1>
<div class="row" style="margin-top: 40px;"> <div class="row" style="margin-top: 40px;">
<div class="col-lg-2 col-md-12 col-sm-12 col-xs-12"></div> <div class="col-lg-2 col-md-12 col-sm-12 col-xs-12"></div>
<div class="col-lg-2 col-md-6 col-sm-6 col-xs-12"> <div class="col-lg-2 col-md-6 col-sm-6 col-xs-12">
<a href="https://gitlab.com/digitaldemocratic/digitaldemocratic" target="_blank" style="color: deepskyblue"> <a href="https:/gitlab.com/digitaldemocratic/digitaldemocratic" target="_blank" style="color: deepskyblue">
<i class="fa fa-globe" style="font-size: 125px;" aria-hidden="true"></i> <i class="fa fa-globe" style="font-size: 125px;" aria-hidden="true"></i>
<h1><small>Visit website</small></h1> <h1><small>Visit website</small></h1>
</a> </a>
</div> </div>
<div class="col-lg-2 col-md-6 col-sm-6 col-xs-12"> <div class="col-lg-2 col-md-6 col-sm-6 col-xs-12">
<a href="https://gitlab.com/digitaldemocratic/digitaldemocratic/-/issues" target="_blank" style="color: orange"> <a href="https:/gitlab.com/digitaldemocratic/digitaldemocratic/-/issues" target="_blank" style="color: orange">
<i class="fa fa-gitlab fa-5x" style="font-size: 125px;" aria-hidden="true"></i> <i class="fa fa-gitlab fa-5x" style="font-size: 125px;" aria-hidden="true"></i>
<h1><small>Open an issue</small></h1> <h1><small>Open an issue</small></h1>
</a> </a>
@ -38,7 +38,7 @@
</h1> </h1>
</div> </div>
<div class="col-md-2 col-sm-6 col-xs-12"> <div class="col-md-2 col-sm-6 col-xs-12">
<p><img src="/isard-sso-admin/static/img/agplv3-155x51.png" style="margin-top: 60px;"></p> <p><img src="/static/img/agplv3-155x51.png" style="margin-top: 60px;"></p>
<h1 style="margin-top: 25px;"><small>License</small></h1> <h1 style="margin-top: 25px;"><small>License</small></h1>
</div> </div>
<div class="col-md-4 col-sm-4 col-xs-4"></div> <div class="col-md-4 col-sm-4 col-xs-4"></div>
@ -51,7 +51,7 @@
{% endblock %} {% endblock %}
{% block pagescript %} {% block pagescript %}
<script src="/isard-sso-admin/static/js/restful.js"></script> <script src="/static/js/restful.js"></script>
<script src="/isard-sso-admin/static/js/status_socket.js"></script> <script src="/static/js/status_socket.js"></script>
{% endblock %} {% endblock %}

View File

@ -2,9 +2,9 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block css %} {% block css %}
<!-- Ion.RangeSlider --> <!-- Ion.RangeSlider -->
<link href="/isard-sso-admin/vendors/normalize-css/normalize.css" rel="stylesheet"> <link href="/vendors/normalize-css/normalize.css" rel="stylesheet">
<!-- Switchery --> <!-- Switchery -->
<link href="/isard-sso-admin/vendors/switchery/dist/switchery.min.css" rel="stylesheet"> <link href="/vendors/switchery/dist/switchery.min.css" rel="stylesheet">
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -49,11 +49,11 @@
{% block pagescript %} {% block pagescript %}
<!-- Ion.RangeSlider --> <!-- Ion.RangeSlider -->
<script src="/isard-sso-admin/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script> <script src="/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script>
<!-- iCheck --> <!-- iCheck -->
<script src="/isard-sso-admin/vendors/iCheck/icheck.min.js"></script> <script src="/vendors/iCheck/icheck.min.js"></script>
<!-- Switchery --> <!-- Switchery -->
<script src="/isard-sso-admin/vendors/switchery/dist/switchery.min.js"></script> <script src="/vendors/switchery/dist/switchery.min.js"></script>
<!-- Desktops sse & modals --> <!-- Desktops sse & modals -->
<script src="/isard-sso-admin/static/js/groups.js"></script> <script src="/static/js/groups.js"></script>
{% endblock %} {% endblock %}

View File

@ -115,6 +115,11 @@
<div class="modal-body"> <div class="modal-body">
<form id="modalPasswdUserForm" class="form-inline form-label-left"> <form id="modalPasswdUserForm" class="form-inline form-label-left">
<div class="x_content" style="padding: 0px;"> <div class="x_content" style="padding: 0px;">
<div class="row">
This is a proposed password.<br>
Change it for the one desired for this user. <br>
REMEMBER TO COPY THIS PASSWORD AS IT IS THE ONLY TIME YOU WILL SEE IT
</div>
<div class="row"> <div class="row">
<div class="col-md-12 col-xs-12"> <div class="col-md-12 col-xs-12">
<input id="id" hidden/> <input id="id" hidden/>
@ -211,7 +216,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-12 col-xs-12"> <div class="col-md-12 col-xs-12">
<label class="control-label" for="id">Group</label> <label class="control-label" for="id">Groups</label>
<select class="groups-select roundbox" name="groups[]" multiple="multiple" style="width:100%"> <select class="groups-select roundbox" name="groups[]" multiple="multiple" style="width:100%">
</select> </select>
</div> </div>
@ -225,7 +230,7 @@
</div> </div>
<div class="x_content" style="padding: 0px;"> <div class="x_content" style="padding: 0px;">
<div class="row"> <div class="row">
<div class="col-md-4 col-xs-12"> <!-- <div class="col-md-4 col-xs-12">
<label class="control-label" for="id">Moodle</label> <label class="control-label" for="id">Moodle</label>
<select class="role-moodle-select" name="moodle" style="width:100%"> <select class="role-moodle-select" name="moodle" style="width:100%">
</select> </select>
@ -234,9 +239,9 @@
<label class="control-label" for="id">Nextcloud</label> <label class="control-label" for="id">Nextcloud</label>
<select class="role-nextcloud-select" name="nextcloud" style="width:100%"> <select class="role-nextcloud-select" name="nextcloud" style="width:100%">
</select> </select>
</div> </div> -->
<div class="col-md-4 col-xs-12"> <div class="col-md-4 col-xs-12">
<label class="control-label" for="id">Keycloak</label> <label class="control-label" for="id">Role</label>
<select class="role-keycloak-select" name="keycloak" style="width:100%"> <select class="role-keycloak-select" name="keycloak" style="width:100%">
</select> </select>
</div> </div>

View File

@ -2,9 +2,9 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block css %} {% block css %}
<!-- Ion.RangeSlider --> <!-- Ion.RangeSlider -->
<link href="/isard-sso-admin/vendors/normalize-css/normalize.css" rel="stylesheet"> <link href="/vendors/normalize-css/normalize.css" rel="stylesheet">
<!-- Switchery --> <!-- Switchery -->
<link href="/isard-sso-admin/vendors/switchery/dist/switchery.min.css" rel="stylesheet"> <link href="/vendors/switchery/dist/switchery.min.css" rel="stylesheet">
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -42,11 +42,11 @@
{% block pagescript %} {% block pagescript %}
<!-- Ion.RangeSlider --> <!-- Ion.RangeSlider -->
<script src="/isard-sso-admin/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script> <script src="/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script>
<!-- iCheck --> <!-- iCheck -->
<script src="/isard-sso-admin/vendors/iCheck/icheck.min.js"></script> <script src="/vendors/iCheck/icheck.min.js"></script>
<!-- Switchery --> <!-- Switchery -->
<script src="/isard-sso-admin/vendors/switchery/dist/switchery.min.js"></script> <script src="/vendors/switchery/dist/switchery.min.js"></script>
<!-- Desktops sse & modals --> <!-- Desktops sse & modals -->
<script src="/isard-sso-admin/static/js/roles.js"></script> <script src="/static/js/roles.js"></script>
{% endblock %} {% endblock %}

View File

@ -2,9 +2,9 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block css %} {% block css %}
<!-- Ion.RangeSlider --> <!-- Ion.RangeSlider -->
<link href="/isard-sso-admin/vendors/normalize-css/normalize.css" rel="stylesheet"> <link href="/vendors/normalize-css/normalize.css" rel="stylesheet">
<!-- Switchery --> <!-- Switchery -->
<link href="/isard-sso-admin/vendors/switchery/dist/switchery.min.css" rel="stylesheet"> <link href="/vendors/switchery/dist/switchery.min.css" rel="stylesheet">
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -72,16 +72,16 @@
</div> </div>
</div> </div>
{% include 'pages/modals/external_modals.html' %} {% include 'pages/sysadmin/modals/external_modals.html' %}
{% endblock %} {% endblock %}
{% block pagescript %} {% block pagescript %}
<!-- Ion.RangeSlider --> <!-- Ion.RangeSlider -->
<script src="/isard-sso-admin/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script> <script src="/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script>
<!-- iCheck --> <!-- iCheck -->
<script src="/isard-sso-admin/vendors/iCheck/icheck.min.js"></script> <script src="/vendors/iCheck/icheck.min.js"></script>
<!-- Switchery --> <!-- Switchery -->
<script src="/isard-sso-admin/vendors/switchery/dist/switchery.min.js"></script> <script src="/vendors/switchery/dist/switchery.min.js"></script>
<!-- Desktops sse & modals --> <!-- Desktops sse & modals -->
<script src="/isard-sso-admin/static/js/external.js"></script> <script src="/static/js/external.js"></script>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,81 @@
<!-- extend base layout -->
{% extends "base.html" %}
{% block css %}
<!-- Ion.RangeSlider -->
<link href="/vendors/normalize-css/normalize.css" rel="stylesheet">
<!-- Switchery -->
<link href="/vendors/switchery/dist/switchery.min.css" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12 col-sm-12 col-xs-12">
<div class="x_panel">
<div class="x_title">
<h3><i class="fa fa-user"></i> Users</h3>
<ul class="nav navbar-right panel_toolbox">
<li>
<a class="btn-new-user"><span style="color: #5499c7; "><i class="fa fa-plus"></i> Add new</span></a>
</li>
</ul>
<div class="clearfix"></div>
</div>
<div class="x_content">
<button class="btn btn-primary btn-xs btn-sync_to_nextcloud">
<i class="fa fa-refresh" aria-hidden="true"></i> Sync to Nextcloud
</button>
<button class="btn btn-primary btn-xs btn-sync_to_moodle">
<i class="fa fa-refresh" aria-hidden="true"></i> Sync to Moodle
</button>
{% if current_user.role =='sysadmin' %}
<button class="btn btn-danger btn-xs btn-delete_keycloak">
<i class="fa fa-trash"></i> Delete all keycloak
</button>
<button class="btn btn-danger btn-xs btn-delete_nextcloud">
<i class="fa fa-trash"></i> Delete missing keycloak in nextcloud
</button>
<button class="btn btn-danger btn-xs btn-delete_moodle">
<i class="fa fa-trash"></i> Delete missing keycloak in moodle
</button>
{% endif %}
<table id="users" class="table" width="100%">
<thead>
<tr>
<th></th>
<th>Avatar</th>
<th>Username</th>
<th>First</th>
<th>Last</th>
<th>email</th>
<th>Keycloak</th>
<th>K.Groups</th>
<th>K.Roles</th>
<th>Moodle</th>
<th>M.Groups</th>
<th>Nextcloud</th>
<th>N.Groups</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
{% include 'pages/modals/users_modals.html' %}
{% include 'pages/users_detail.html' %}
</div>
</div>
{% endblock %}
{% block pagescript %}
<!-- Ion.RangeSlider -->
<script src="/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script>
<!-- iCheck -->
<script src="/vendors/iCheck/icheck.min.js"></script>
<!-- Switchery -->
<script src="/vendors/switchery/dist/switchery.min.js"></script>
<!-- Desktops sse & modals -->
<script src="/static/js/sysadmin/users.js"></script>
{% endblock %}

View File

@ -2,9 +2,9 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block css %} {% block css %}
<!-- Ion.RangeSlider --> <!-- Ion.RangeSlider -->
<link href="/isard-sso-admin/vendors/normalize-css/normalize.css" rel="stylesheet"> <link href="/vendors/normalize-css/normalize.css" rel="stylesheet">
<!-- Switchery --> <!-- Switchery -->
<link href="/isard-sso-admin/vendors/switchery/dist/switchery.min.css" rel="stylesheet"> <link href="/vendors/switchery/dist/switchery.min.css" rel="stylesheet">
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -21,13 +21,13 @@
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="x_content"> <div class="x_content">
{% if current_user.role =='admin' %}
<button class="btn btn-primary btn-xs btn-sync_to_nextcloud"> <button class="btn btn-primary btn-xs btn-sync_to_nextcloud">
<i class="fa fa-refresh" aria-hidden="true"></i> Sync to Nextcloud <i class="fa fa-refresh" aria-hidden="true"></i> Sync to Nextcloud
</button> </button>
<button class="btn btn-primary btn-xs btn-sync_to_moodle"> <button class="btn btn-primary btn-xs btn-sync_to_moodle">
<i class="fa fa-refresh" aria-hidden="true"></i> Sync to Moodle <i class="fa fa-refresh" aria-hidden="true"></i> Sync to Moodle
</button> </button>
{% if current_user.role =='admin-keycloak' %}
<button class="btn btn-danger btn-xs btn-delete_keycloak"> <button class="btn btn-danger btn-xs btn-delete_keycloak">
<i class="fa fa-trash"></i> Delete all keycloak <i class="fa fa-trash"></i> Delete all keycloak
</button> </button>
@ -43,17 +43,15 @@
<tr> <tr>
<th></th> <th></th>
<th>Avatar</th> <th>Avatar</th>
<th>Enabled</th>
<th>Username</th> <th>Username</th>
<th>Actions</th>
<th>First</th> <th>First</th>
<th>Last</th> <th>Last</th>
<th>email</th> <th>Email</th>
<th>Keycloak</th> <th>Groups</th>
<th>K.Groups</th> <th>Roles</th>
<th>K.Roles</th>
<th>Moodle</th>
<th>M.Groups</th>
<th>Nextcloud</th>
<th>N.Groups</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -71,11 +69,11 @@
{% block pagescript %} {% block pagescript %}
<!-- Ion.RangeSlider --> <!-- Ion.RangeSlider -->
<script src="/isard-sso-admin/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script> <script src="/vendors/ion.rangeSlider/js/ion.rangeSlider.min.js"></script>
<!-- iCheck --> <!-- iCheck -->
<script src="/isard-sso-admin/vendors/iCheck/icheck.min.js"></script> <script src="/vendors/iCheck/icheck.min.js"></script>
<!-- Switchery --> <!-- Switchery -->
<script src="/isard-sso-admin/vendors/switchery/dist/switchery.min.js"></script> <script src="/vendors/switchery/dist/switchery.min.js"></script>
<!-- Desktops sse & modals --> <!-- Desktops sse & modals -->
<script src="/isard-sso-admin/static/js/users.js"></script> <script src="/static/js/users.js"></script>
{% endblock %} {% endblock %}

View File

@ -1,6 +1,6 @@
<div class="navbar nav_title" style="border: 0;"> <div class="navbar nav_title" style="border: 0;">
<a href="/isard-sso-admin/about" class="site_title"> <a href="/about" class="site_title">
<img src="/isard-sso-admin/static/img/dd.svg" class="sidebar_logo logo_white" alt="dd"> <img src="/custom/img/logo.png" class="sidebar_logo logo_white" alt="dd">
<span>Digital Democratic</span> <span>Digital Democratic</span>
</a> </a>
</div> </div>
@ -13,13 +13,19 @@
<div class="clearfix"></div> <div class="clearfix"></div>
<ul class="nav side-menu"> <ul class="nav side-menu">
<li><a class="btn-global-resync"><span style="color: #c75454; "><i class="fa fa-refresh"></i> Resync</span></a></li> <li><a class="btn-global-resync"><span style="color: #c75454; "><i class="fa fa-refresh"></i> Resync</span></a></li>
<li><a href="/isard-sso-admin/users"><i class="fa fa-user"></i> Users</a></li> <li><a href="/users"><i class="fa fa-user"></i> Users</a></li>
<li><a href="/isard-sso-admin/groups"><i class="fa fa-users"></i> Groups</a></li> <li><a href="/groups"><i class="fa fa-users"></i> Groups</a></li>
<li><a href="/isard-sso-admin/roles"><i class="fa fa-user-secret"></i> Roles</a></li> <li><a href="/roles"><i class="fa fa-user-secret"></i> Roles</a></li>
<li><a href="/isard-sso-admin/external"><i class="fa fa-external-link"></i> External</a></li>
{% if current_user.role == 'admin' %}
<h3>System Admin</h3>
<div class="clearfix"></div>
<li><a href="/sysadmin/users"><i class="fa fa-user"></i> SysAdminUsers</span></a></li>
<li><a href="/sysadmin/external"><i class="fa fa-external-link"></i> External</a></li>
{% endif %}
</ul> </ul>
<ul class="nav side-menu"> <ul class="nav side-menu">
<li><a href="/isard-sso-admin/about"><i class="fa fa-question"></i> About</span></a> <li><a href="/about"><i class="fa fa-question"></i> About</span></a>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -0,0 +1,145 @@
#!flask/bin/python
# coding=utf-8
from admin import app
import logging as log
import traceback
from uuid import uuid4
import time,json
import sys,os
from flask import render_template, Response, request, redirect, url_for, jsonify
import concurrent.futures
from flask_login import login_required
# import Queue
import threading
threads={}
# q = Queue.Queue()
from pprint import pprint
from keycloak.exceptions import KeycloakGetError
@app.route('/api/resync')
@login_required
def resync():
return json.dumps(app.admin.resync_data()), 200, {'Content-Type': 'application/json'}
@app.route('/api/users', methods=['GET'])
@app.route('/api/users/<provider>', methods=['POST', 'PUT', 'GET', 'DELETE'])
@login_required
def users(provider=False):
if request.method == 'DELETE':
if provider == 'keycloak':
return json.dumps(app.admin.delete_keycloak_users()), 200, {'Content-Type': 'application/json'}
if provider == 'nextcloud':
return json.dumps(app.admin.delete_nextcloud_users()), 200, {'Content-Type': 'application/json'}
if provider == 'moodle':
return json.dumps(app.admin.delete_moodle_users()), 200, {'Content-Type': 'application/json'}
if request.method == 'POST':
if provider == 'moodle':
return json.dumps(app.admin.sync_to_moodle()), 200, {'Content-Type': 'application/json'}
if provider == 'nextcloud':
return json.dumps(app.admin.sync_to_nextcloud()), 200, {'Content-Type': 'application/json'}
return json.dumps(app.admin.get_mix_users()), 200, {'Content-Type': 'application/json'}
# Update pwd
@app.route('/api/user_password', methods=['GET'])
@app.route('/api/user_password/<userid>', methods=['PUT'])
@login_required
def user_password(userid=False):
if request.method == 'GET':
return json.dumps(app.admin.get_dice_pwd()), 200, {'Content-Type': 'application/json'}
if request.method == 'PUT':
data=request.get_json(force=True)
password=data['password']
temporary=data.get('temporary',True)
try:
res = app.admin.user_update_password(userid,password,temporary)
return json.dumps({}), 200, {'Content-Type': 'application/json'}
except KeycloakGetError as e:
print(e.error_message.decode("utf-8"))
return e.error_message, e.response_code, {'Content-Type': 'application/json'}
return json.dumps({}), 301, {'Content-Type': 'application/json'}
# User
@app.route('/api/user/<userid>', methods=['POST', 'PUT', 'GET', 'DELETE'])
@login_required
def user(userid=None):
if request.method == 'DELETE':
res = app.admin.delete_user(userid)
return json.dumps({}), 200, {'Content-Type': 'application/json'}
# return json.dumps(), 301, {'Content-Type': 'application/json'}
if request.method == 'POST':
pass
if request.method == 'PUT':
pass
if request.method == 'DELETE':
pass
if request.method == 'GET':
res = app.admin.get_user(userid)
return json.dumps(res), 200, {'Content-Type': 'application/json'}
@app.route('/api/roles')
@login_required
def roles():
sorted_roles = sorted(app.admin.get_roles(), key=lambda k: k['name'])
return json.dumps(sorted_roles), 200, {'Content-Type': 'application/json'}
@app.route('/api/groups')
@app.route('/api/groups/<provider>', methods=['POST', 'PUT', 'GET', 'DELETE'])
@login_required
def groups(provider=False):
if request.method == 'GET':
sorted_groups = sorted(app.admin.get_mix_groups(), key=lambda k: k['name'])
return json.dumps(sorted_groups), 200, {'Content-Type': 'application/json'}
if request.method == 'DELETE':
if provider == 'keycloak':
return json.dumps(app.admin.delete_keycloak_groups()), 200, {'Content-Type': 'application/json'}
### SYSADM USERS ONLY
@app.route('/api/external', methods=['POST', 'PUT', 'GET'])
@login_required
def external():
if 'external' in threads.keys():
if threads['external'] is not None and threads['external'].is_alive():
return json.dumps({}), 301, {'Content-Type': 'application/json'}
else:
threads['external']=None
data=request.get_json(force=True)
if request.method == 'POST':
if data['format']=='json-ga':
threads['external'] = threading.Thread(target=app.admin.upload_json_ga, args=(data,))
threads['external'].start()
return json.dumps({}), 200, {'Content-Type': 'application/json'}
if data['format']=='csv-ug':
threads['external'] = threading.Thread(target=app.admin.upload_csv_ug, args=(data,))
threads['external'].start()
return json.dumps({}), 200, {'Content-Type': 'application/json'}
if request.method == 'PUT':
threads['external'] = threading.Thread(target=app.admin.sync_external, args=(data,))
threads['external'].start()
return json.dumps({}), 200, {'Content-Type': 'application/json'}
return json.dumps({}), 500, {'Content-Type': 'application/json'}
@app.route('/api/external/users')
@login_required
def external_users_list():
return json.dumps(app.admin.get_external_users()), 200, {'Content-Type': 'application/json'}
@app.route('/api/external/groups')
@login_required
def external_groups_list():
return json.dumps(app.admin.get_external_groups()), 200, {'Content-Type': 'application/json'}
@app.route('/api/external/roles', methods=['PUT'])
@login_required
def external_roles():
if request.method == 'PUT':
return json.dumps(app.admin.external_roleassign(request.get_json(force=True))), 200, {'Content-Type': 'application/json'}

View File

@ -1,25 +0,0 @@
#!flask/bin/python
# coding=utf-8
from admin import app
import logging as log
import traceback
from uuid import uuid4
import time,json
import sys,os
from flask import render_template, Response, request, redirect, url_for, jsonify, send_file
from flask_login import login_required
from pprint import pprint
from ..lib.avatars import Avatars
avatars=Avatars()
@app.route('/isard-sso-admin/avatar/<username>', methods=['GET'])
@login_required
def avatar(username):
userid=avatars.username2id(username)
if userid:
return send_file('../avatars/master-avatars/'+userid, mimetype='image/jpeg')
return send_file('static/img/usera.jpg', mimetype='image/jpeg')

View File

@ -5,7 +5,8 @@ from flask import render_template, flash, request, redirect, url_for
from ..auth.authentication import * from ..auth.authentication import *
from flask_login import login_required, current_user, login_user, logout_user from flask_login import login_required, current_user, login_user, logout_user
@app.route('/isard-sso-admin/login', methods=['GET', 'POST']) @app.route('/', methods=['GET', 'POST'])
@app.route('/login', methods=['GET', 'POST'])
def login(): def login():
if request.method == 'POST': if request.method == 'POST':
if request.form['user'] == '' or request.form['password'] == '': if request.form['user'] == '' or request.form['password'] == '':
@ -18,12 +19,12 @@ def login():
user=User({'id': ram_user['id'], 'password': ram_user['password'], 'role': ram_user['role'], 'active': True}) user=User({'id': ram_user['id'], 'password': ram_user['password'], 'role': ram_user['role'], 'active': True})
login_user(user) login_user(user)
flash('Logged in successfully.','success') flash('Logged in successfully.','success')
return redirect(url_for('users')) return redirect(url_for('web_users'))
else: else:
flash('Username not found or incorrect password.','warning') flash('Username not found or incorrect password.','warning')
return render_template('login.html') return render_template('login.html')
@app.route('/isard-sso-admin/logout', methods=['GET']) @app.route('/logout', methods=['GET'])
@login_required @login_required
def logout(): def logout():
logout_user() logout_user()

View File

@ -1,144 +0,0 @@
#!flask/bin/python
# coding=utf-8
from admin import app
import logging as log
import traceback
from uuid import uuid4
import time,json
import sys,os
from flask import render_template, Response, request, redirect, url_for, jsonify
import concurrent.futures
from flask_login import login_required
from pprint import pprint
# from flask_socketio import SocketIO, emit, join_room, leave_room, \
# close_room, rooms, disconnect, send
# socketio = SocketIO(app)
# from flask_login import login_required
# from flask_oidc import OpenIDConnect
from ..auth.authentication import oidc
@app.route('/isard-sso-admin/custom_callback')
@oidc.custom_callback
def callback(data):
return 'Hello. You submitted %s' % data
@app.route('/isard-sso-admin/private')
@oidc.require_login
def hello_me():
info = oidc.user_getinfo(['email', 'openid_id'])
return ('Hello, %s (%s)! <a href="/">Return</a>' %
(info.get('email'), info.get('openid_id')))
@app.route('/isard-sso-admin/api')
@oidc.accept_token(True, ['openid'])
def hello_api():
return json.dumps({'hello': 'Welcome %s' % g.oidc_token_info['sub']})
@app.route('/isard-sso-admin/logout')
def logoutoidc():
oidc.logout()
return 'Hi, you have been logged out! <a href="/">Return</a>'
@app.route('/isard-sso-admin/resync')
@login_required
def resync():
return json.dumps(app.admin.resync_data()), 200, {'Content-Type': 'application/json'}
@app.route('/isard-sso-admin/users', methods=['GET'])
@app.route('/isard-sso-admin/users/<provider>', methods=['POST', 'PUT', 'GET', 'DELETE'])
@login_required
def users(provider=False):
if request.method == 'DELETE':
if provider == 'keycloak':
return json.dumps(app.admin.delete_keycloak_users()), 200, {'Content-Type': 'application/json'}
if provider == 'nextcloud':
return json.dumps(app.admin.delete_nextcloud_users()), 200, {'Content-Type': 'application/json'}
if provider == 'moodle':
return json.dumps(app.admin.delete_moodle_users()), 200, {'Content-Type': 'application/json'}
if request.method == 'POST':
if provider == 'moodle':
return json.dumps(app.admin.sync_to_moodle()), 200, {'Content-Type': 'application/json'}
if provider == 'nextcloud':
return json.dumps(app.admin.sync_to_nextcloud()), 200, {'Content-Type': 'application/json'}
return render_template('pages/users.html', title="Users", nav="Users")
@app.route('/isard-sso-admin/users_list')
@login_required
def users_list():
return json.dumps(app.admin.get_mix_users()), 200, {'Content-Type': 'application/json'}
@app.route('/isard-sso-admin/roles')
@login_required
def roles():
return render_template('pages/roles.html', title="Roles", nav="Roles")
@app.route('/isard-sso-admin/roles_list')
@login_required
def roles_list():
return json.dumps(app.admin.get_roles()), 200, {'Content-Type': 'application/json'}
@app.route('/isard-sso-admin/groups')
@app.route('/isard-sso-admin/groups/<provider>', methods=['POST', 'PUT', 'GET', 'DELETE'])
@login_required
def groups(provider=False):
if request.method == 'DELETE':
if provider == 'keycloak':
return json.dumps(app.admin.delete_keycloak_groups()), 200, {'Content-Type': 'application/json'}
return render_template('pages/groups.html', title="Groups", nav="Groups")
@app.route('/isard-sso-admin/groups_list')
@login_required
def groups_list():
return json.dumps(app.admin.get_mix_groups()), 200, {'Content-Type': 'application/json'}
@app.route('/isard-sso-admin/external', methods=['POST', 'PUT', 'GET'])
@login_required
def external():
if request.method == 'POST':
data=request.get_json(force=True)
if data['format']=='json-ga':
app.admin.upload_json_ga(data)
if data['format']=='csv-ug':
app.admin.upload_csv_ug(data)
return json.dumps({}), 200, {'Content-Type': 'application/json'}
if request.method == 'PUT':
return json.dumps(app.admin.sync_external(request.get_json(force=True))), 200, {'Content-Type': 'application/json'}
return render_template('pages/external.html', title="External", nav="External")
@app.route('/isard-sso-admin/external_users_list')
@login_required
def external_users_list():
return json.dumps(app.admin.get_external_users()), 200, {'Content-Type': 'application/json'}
@app.route('/isard-sso-admin/external_groups_list')
@login_required
def external_groups_list():
return json.dumps(app.admin.get_external_groups()), 200, {'Content-Type': 'application/json'}
@app.route('/isard-sso-admin/external/roles', methods=['PUT'])
@login_required
def external_roles():
if request.method == 'PUT':
return json.dumps(app.admin.external_roleassign(request.get_json(force=True))), 200, {'Content-Type': 'application/json'}
def WaitStatus(self, desktop_id, original_status, transition_status, final_status):
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(lambda p: self._wait_for_domain_status(*p), [desktop_id, original_status, transition_status, final_status])
try:
result = future.result()
except ReqlTimeoutError:
raise DesktopActionTimeout
except DesktopWaitFailed:
raise DesktopActionFailed
return True

View File

@ -6,14 +6,14 @@
# # socketio = SocketIO(app) # # socketio = SocketIO(app)
# # from ...start import socketio # # from ...start import socketio
# @socketio.on('connect', namespace='/isard-sso-admin/sio') # @socketio.on('connect', namespace='//sio')
# def socketio_connect(): # def socketio_connect():
# join_room('admin') # join_room('admin')
# socketio.emit('update', # socketio.emit('update',
# json.dumps('Joined'), # json.dumps('Joined'),
# namespace='/isard-sso-admin/sio', # namespace='//sio',
# room='admin') # room='admin')
# @socketio.on('disconnect', namespace='/isard-sso-admin/sio') # @socketio.on('disconnect', namespace='//sio')
# def socketio_domains_disconnect(): # def socketio_domains_disconnect():
# None # None

View File

@ -0,0 +1,80 @@
#!flask/bin/python
# coding=utf-8
from admin import app
import logging as log
import traceback
from uuid import uuid4
import time,json
import sys,os
from flask import render_template, Response, request, redirect, url_for, jsonify, send_file
import concurrent.futures
from flask_login import login_required
from pprint import pprint
from ..lib.avatars import Avatars
avatars=Avatars()
''' OIDC TESTS '''
# from ..auth.authentication import oidc
# @app.route('/custom_callback')
# @oidc.custom_callback
# def callback(data):
# return 'Hello. You submitted %s' % data
# @app.route('/private')
# @oidc.require_login
# def hello_me():
# info = oidc.user_getinfo(['email', 'openid_id'])
# return ('Hello, %s (%s)! <a href="/">Return</a>' %
# (info.get('email'), info.get('openid_id')))
# @app.route('/api')
# @oidc.accept_token(True, ['openid'])
# def hello_api():
# return json.dumps({'hello': 'Welcome %s' % g.oidc_token_info['sub']})
# @app.route('/logout')
# def logoutoidc():
# oidc.logout()
# return 'Hi, you have been logged out! <a href="/">Return</a>'
''' OIDC TESTS '''
@app.route('/users')
@login_required
def web_users():
return render_template('pages/users.html', title="Users", nav="Users")
@app.route('/roles')
@login_required
def web_roles():
return render_template('pages/roles.html', title="Roles", nav="Roles")
@app.route('/groups')
@login_required
def web_groups(provider=False):
return render_template('pages/groups.html', title="Groups", nav="Groups")
@app.route('/avatar/<userid>', methods=['GET'])
@login_required
def avatar(userid):
if userid != 'false':
return send_file('../avatars/master-avatars/'+userid, mimetype='image/jpeg')
return send_file('static/img/missing.jpg', mimetype='image/jpeg')
### SYS ADMIN
@app.route('/sysadmin/users')
@login_required
def web_sysadmin_users():
return render_template('pages/sysadmin/users.html', title="SysAdmin Users", nav="SysAdminUsers")
@app.route('/sysadmin/external')
@login_required
## SysAdmin role
def web_sysadmin_external():
return render_template('pages/sysadmin/external.html', title="External", nav="External")

View File

@ -1,13 +1,13 @@
{ {
"web": { "web": {
"auth_uri": "https://sso.santantoni.duckdns.org/auth/realms/master/protocol/openid-connect/auth", "auth_uri": "https://sso.[[DOMAIN]]/auth/realms/master/protocol/openid-connect/auth",
"client_id": "adminapp", "client_id": "adminapp",
"client_secret": "8a9e5a2e-3be9-43e3-9c47-1796f0d5ab72", "client_secret": "8a9e5a2e-3be9-43e3-9c47-1796f0d5ab72",
"redirect_uris": [ "redirect_uris": [
"https://sso.santantoni.duckdns.org/isard-sso-admin/custom_callback" "https://sso.[[DOMAIN]]/oidc_callback"
], ],
"userinfo_uri": "https://sso.santantoni.duckdns.org/auth/realms/master/protocol/openid-connect/userinfo", "userinfo_uri": "https://sso.[[DOMAIN]]/auth/realms/master/protocol/openid-connect/userinfo",
"token_uri": "https://sso.santantoni.duckdns.org/auth/realms/master/protocol/openid-connect/token", "token_uri": "https://sso.[[DOMAIN]]/auth/realms/master/protocol/openid-connect/token",
"token_introspection_uri": "https://sso.santantoni.duckdns.org/auth/realms/master/protocol/openid-connect/token/introspect" "token_introspection_uri": "https://sso.[[DOMAIN]]/auth/realms/master/protocol/openid-connect/token/introspect"
} }
} }

View File

@ -8,34 +8,21 @@ from flask_socketio import SocketIO, emit, join_room, leave_room, \
import json import json
from admin import app from admin import app
# socketio.init_app(app, cors_allowed_origins="*")
# from admin.views.Socketio import *
app.socketio = SocketIO(app) app.socketio = SocketIO(app)
# app.socketio.init_app(app, cors_allowed_origins="*") @app.socketio.on('connect', namespace='/sio')
@app.socketio.on('connect', namespace='/isard-sso-admin/sio')
def socketio_connect(): def socketio_connect():
join_room('admin') join_room('admin')
app.socketio.emit('update', app.socketio.emit('update',
json.dumps('Joined'), json.dumps('Joined'),
namespace='/isard-sso-admin/sio', namespace='/sio',
room='admin') room='admin')
# socketio.emit('update', @app.socketio.on('disconnect', namespace='/sio')
# json.dumps('DATA SENT'), def socketio_disconnect():
# namespace='/isard-sso-admin/sio',
# room='admin')
@app.socketio.on('disconnect', namespace='/isard-sso-admin/sio')
def socketio_domains_disconnect():
None None
if __name__ == '__main__': if __name__ == '__main__':
app.socketio.run(app,host='0.0.0.0', port=9000, debug=False, cors_allowed_origins="*", ssl_context='adhoc', async_mode="threading") #, logger=logger, engineio_logger=engineio_logger) app.socketio.run(app,host='0.0.0.0', port=9000, debug=False, ssl_context='adhoc', async_mode="threading") #, logger=logger, engineio_logger=engineio_logger)
# , cors_allowed_origins="*"
# /usr/lib/python3.8/site-packages/certifi # /usr/lib/python3.8/site-packages/certifi

View File

@ -47,6 +47,7 @@ frontend website
acl is_sso hdr_beg(host) sso. acl is_sso hdr_beg(host) sso.
acl is_ipa hdr_beg(host) ipa. acl is_ipa hdr_beg(host) ipa.
acl is_api hdr_beg(host) api. acl is_api hdr_beg(host) api.
acl is_admin hdr_beg(host) admin.
use_backend be_nextcloud if is_nextcloud use_backend be_nextcloud if is_nextcloud
use_backend be_moodle if is_moodle use_backend be_moodle if is_moodle
@ -56,7 +57,7 @@ frontend website
use_backend be_etherpad if is_pad use_backend be_etherpad if is_pad
use_backend be_admin if is_sso { path_beg /socket.io } use_backend be_admin if is_sso { path_beg /socket.io }
use_backend be_adminer if is_sso { path_beg /isard-sso-adminer } use_backend be_adminer if is_sso { path_beg /isard-sso-adminer }
use_backend be_admin if is_sso { path_beg /isard-sso-admin } use_backend be_admin if is_admin
use_backend be_sso if is_sso use_backend be_sso if is_sso
use_backend be_ipa if is_ipa use_backend be_ipa if is_ipa
use_backend be_api if is_api use_backend be_api if is_api