31 Commits
Author SHA1 Message Date
aitzol 4f0097ac1b dockerizazioa 2023-12-15 23:21:56 +01:00
aitzol 645edec125 dockerizazioa 2023-12-15 13:11:40 +01:00
aitzol 253c5c2491 lastlogin attribute 2023-11-29 23:30:29 +01:00
aitzol 48be235fde devices, ip attributes 2023-11-29 23:10:24 +01:00
aitzol 5f3bbd0ff4 aldaketa izen ematean 2023-11-28 09:42:46 +01:00
aitzol 249b0009a3 itzulpenak 2023-11-27 15:57:50 +01:00
aitzol 5225f72ee0 itzulpenak 2023-11-27 15:38:45 +01:00
aitzol de1315db33 itzulpenak 2023-11-27 13:34:20 +01:00
aitzol 6c2a65221c v0.0.3 2023-11-27 05:43:19 +01:00
aitzol 407fa6351d v0.0.3 2023-11-26 18:49:05 +01:00
aitzol 490e9bb80c v0.0.3 2023-11-26 18:14:50 +01:00
aitzol c20a8a0b2c v0.0.3 2023-11-25 16:21:11 +01:00
aitzol 05821df546 2fa-2.0 2023-11-25 15:58:37 +01:00
aitzol 3a88b7db3b 2fa-1.4 2023-11-24 23:31:01 +01:00
aitzol a53f2aa274 2fa-1.3 2023-11-24 20:25:35 +01:00
aitzol 92bc2bbdbf 2fa-1.3 2023-11-24 20:06:38 +01:00
aitzol e2d0e7a95e 2fa-1.2 2023-11-24 08:15:40 +01:00
aitzol 9b4bd725da 2fa-1.2 2023-11-24 08:12:00 +01:00
aitzol b783617335 2fa-1.1 2023-11-24 07:46:08 +01:00
aitzol 4ea6831be2 2fa-1.0 2023-11-23 23:14:42 +01:00
aitzol b8645473a7 2fa-1.0 2023-11-23 23:11:03 +01:00
aitzol 9cc6bf6290 2fa-1.0 2023-11-23 23:03:44 +01:00
aitzol 4fe1bec588 2fa-0.9 2023-11-22 20:45:37 +01:00
aitzol f2ffabad3e 2fa-0.9 2023-11-22 11:33:36 +01:00
aitzol 7e7c599530 2fa-0.9 2023-11-22 08:26:22 +01:00
aitzol 7a7edc1f92 2fa-0.8 2023-11-20 13:01:32 +01:00
aitzol 078af33e77 2fa-0.8 2023-11-20 11:45:49 +01:00
aitzol 94563bfefc 2fa-0.7 2023-11-18 23:03:40 +01:00
aitzol da0114f2f8 2fa-0.7 2023-11-18 20:47:09 +01:00
aitzol cbb7ed4498 2fa-0.7 2023-11-18 20:45:37 +01:00
aitzol 4d2cf64f19 2fa-0.7 2023-11-18 20:44:06 +01:00
21 changed files with 605 additions and 298 deletions
+10 -2
View File
@@ -1,8 +1,16 @@
/settings.ini /settings.ini
/settings.ini.example.original /settings.ini.example.original
/uwsgi.ini /uwsgi.ini
/oharrak.txt
/.env
session
libs/__pycache__
__pycache__
*.db
!.empty
*.swp *.swp
*.swo *.swo
*~ *~
session !static/tmp
libs/__pycache__ static/tmp/*
!static/tmp/.gitkeep
+7 -2
View File
@@ -1,6 +1,8 @@
FROM python:3-alpine FROM python:3-alpine
ARG UID_
ENV UID_=1000 ARG USER_=admin
ARG UID_=1000
RUN apk add --no-cache --upgrade bash RUN apk add --no-cache --upgrade bash
ADD --chown=$UID_:$UID_ . /www ADD --chown=$UID_:$UID_ . /www
@@ -16,4 +18,7 @@ RUN set -e; \
pip install -r requirements.txt; \ pip install -r requirements.txt; \
apk del .build-deps; apk del .build-deps;
RUN adduser -S -D $USER_ -u $UID_
USER $USER_
ENTRYPOINT ["./start.sh"] ENTRYPOINT ["./start.sh"]
+1 -1
View File
@@ -52,7 +52,7 @@ Konfiguraketa fitxategia sortu:
edo edo
docker build -t aitzol/ldap-webui:latest . --build-arg UID_=$UID docker build -t aitzol/ldap-webui:latest . --build-arg UID_=$UID --build-arg USER_=$USER
#### Edukiontzia sortu #### Edukiontzia sortu
+11 -9
View File
@@ -13,32 +13,34 @@
<body> <body>
<main> <main>
<h1>2FA</h1> <h1>{{ str['2fa-title'] }}</h1>
% if data['secureAuth'] == True: % if data['secureAuth'] == True:
<div class="info">{{ str['2fa-info-1'] }}</div>
<div class="qr-code">
<img src="{{'static/tmp/'+data['authCode']+'.png'}}" />
</div>
<form name="disable2faForm" method="post" action="/disable_2fa"> <form name="disable2faForm" method="post" action="/disable_2fa">
<label for="2fa">2FA</label> <label for="code">{{ str['code'] }}</label>
<input id="2fa" name="2fa" type="text" value="{{data['authCode']}}"> <input id="" name="code" type="text" value="{{data['authCode']}}" readonly>
<div class="form-buttons"> <div class="form-buttons">
<a href="/user"><button class="green" type="button">{{ str['back'] }}</button></a> <a href="/user"><button class="green" type="button">{{ str['back'] }}</button></a>
<button class="green" type="submit">Disable</button> <button class="green" type="submit">{{ str['disable'] }}</button>
</div> </div>
</form> </form>
% else: % else:
<div class="info">{{ str['2fa-info-2'] }}</div>
<form name="enable2faForm" method="post" action="/enable_2fa"> <form name="enable2faForm" method="post" action="/enable_2fa">
<label for="2fa">2FA</label> <!--<input id="token" name="token" type="text" value="{{data['authCode']}}" readonly>-->
<input id="2fa" name="2fa" type="text" value="{{data['authCode']}}">
<div class="form-buttons"> <div class="form-buttons">
<a href="/user"><button class="green" type="button">{{ str['back'] }}</button></a> <a href="/user"><button class="green" type="button">{{ str['back'] }}</button></a>
<button class="green" type="submit">Enable</button> <button class="green" type="submit">{{ str['enable'] }}</button>
</div> </div>
</form> </form>
+159 -43
View File
@@ -19,28 +19,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
''' '''
import bottle import bottle
from bottle import get, post, static_file, request, route, template from bottle import get, post, static_file, request, route, template, error
from bottle import SimpleTemplate from bottle import SimpleTemplate
from bottle.ext import beaker #from bottle.ext import beaker
from beaker.middleware import SessionMiddleware
from configparser import ConfigParser from configparser import ConfigParser
from ldap3 import Server, Connection, ALL from ldap3 import Server, Connection, ALL
from ldap3 import SIMPLE, SUBTREE, MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE, ALL_ATTRIBUTES from ldap3 import SIMPLE, SUBTREE, MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE, ALL_ATTRIBUTES
from ldap3.core.exceptions import LDAPBindError, LDAPConstraintViolationResult, \ from ldap3.core.exceptions import LDAPBindError, LDAPConstraintViolationResult, \
LDAPInvalidCredentialsResult, LDAPUserNameIsMandatoryError, \ LDAPInvalidCredentialsResult, LDAPUserNameIsMandatoryError, \
LDAPSocketOpenError, LDAPExceptionError, LDAPAttributeOrValueExistsResult LDAPSocketOpenError, LDAPExceptionError, LDAPAttributeOrValueExistsResult, \
LDAPNoSuchAttributeResult
import logging import logging
from os import getenv, environ, path from os import getenv, environ, path, remove
from libs import flist, slist from libs import flist, slist
from libs.localization import * from libs.localization import *
from libs.helper import tools from libs.helper import tools
import random import random
from user_agents import parse as ua_parse from user_agents import parse as ua_parse
from datetime import datetime from datetime import datetime
import cryptocode
import base64
BASE_DIR = path.dirname(__file__) BASE_DIR = path.dirname(__file__)
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
LOG_FORMAT = '%(asctime)s %(levelname)s: %(message)s' LOG_FORMAT = '%(asctime)s %(levelname)s: %(message)s'
VERSION = '0.0.2' VERSION = '0.0.3'
@get('/') @get('/')
def get_index(): def get_index():
@@ -52,7 +56,7 @@ def get_index():
@get('/user') @get('/user')
def get_index(): def get_index():
try: try:
print(newSession().get()) print('SESSION:',newSession().get())
return user_tpl(data=newSession().get(), str=i18n.str) return user_tpl(data=newSession().get(), str=i18n.str)
except Exception as e: except Exception as e:
return index_tpl(str=i18n.str) return index_tpl(str=i18n.str)
@@ -119,10 +123,19 @@ def get_index():
@get('/_2fa') @get('/_2fa')
def get_index(): def get_index():
try: try:
reload(newSession().get()['username'], None, None)
return _2fa_tpl(data=newSession().get(), str=i18n.str) return _2fa_tpl(data=newSession().get(), str=i18n.str)
except Exception as e: except Exception as e:
return index_tpl(str=i18n.str) return index_tpl(str=i18n.str)
@get('/enable_2fa')
@get('/disable_2fa')
def get_index():
try:
return user_tpl(data=newSession().get(), str=i18n.str)
except Exception as e:
return index_tpl(str=i18n.str)
@post('/user') @post('/user')
def post_user(): def post_user():
form = request.forms.getunicode form = request.forms.getunicode
@@ -144,10 +157,57 @@ def post_user():
LOG.warning("Unsuccessful attempt to login %s: %s" % (form('username'), e)) LOG.warning("Unsuccessful attempt to login %s: %s" % (form('username'), e))
return error(str(e)) return error(str(e))
#if 2fa not chekced || (2fa checked & success) try:
#if(check_2fa_step1(form('username'))):
if(newSession().get()['secureAuth']):
# encrypt and store the credentials
key = tools.key()
data = ';'.join([form('username'),form('password'),newSession().get()['authCode']])
data_enc = cryptocode.encrypt(data, key)
data_to_url = base64.urlsafe_b64encode(str.encode(data_enc))
memo.data = data_enc
memo.key = key
logout(form('username'))
return index_tpl(two_factor_authentication=True, path=data_to_url, str=i18n.str)
except Error as e:
#On error force disable 2fa
add_auth_attribute_step1(form('username'), None, action='disable')
LOG.debug("Two factor authentication has been impossible.")
return error(i18n.msg[29])
return user_tpl(alerts=[('success', '%s %s' % (i18n.msg[1], form('username').capitalize()), 'fadeOut' )], data=newSession().get(), str=i18n.str) return user_tpl(alerts=[('success', '%s %s' % (i18n.msg[1], form('username').capitalize()), 'fadeOut' )], data=newSession().get(), str=i18n.str)
#elif 2fa checked
#return _2fa_tpl(alerts=[('success', '%s %s' % (i18n.msg[1], form('username').capitalize()), 'fadeOut' )], data=newSession().get(), str=i18n.str) @post('/user/<path>')
def post_user_step2(path):
form = request.forms.getunicode
def error(msg):
return index_tpl(alerts=[('error', msg, 'fadeOut')], str=i18n.str)
try:
# decrypt url
path = base64.urlsafe_b64decode(path)
path = cryptocode.decrypt(path.decode('utf-8'), memo.key)
data = path.split(';')
username = data[0]
password = data[1]
secret = data[2]
except:
newSession()
return error(i18n.msg[27])
#if not tools._2fa_validation(form('token'), newSession().get()['authCode']):
if not tools._2fa_validation(form('token'), secret):
return error(i18n.msg[6])
else:
try:
login(username, password)
except Error as e:
LOG.warning("Unsuccessful attempt to login %s: %s" % (username, e))
return error(str(e))
return user_tpl(alerts=[('success', '%s %s' % (i18n.msg[1], newSession().get()['username']), 'fadeOut' )], data=newSession().get(), str=i18n.str)
@post('/signup') @post('/signup')
def post_signup(): def post_signup():
@@ -171,8 +231,12 @@ def post_signup():
def error(msg): def error(msg):
return signup_tpl(alerts=[('error', msg, 'fadeOut')], str=i18n.str) return signup_tpl(alerts=[('error', msg, 'fadeOut')], str=i18n.str)
try:
if not tools.code_is_valid(form('invite_code'), db): if not tools.code_is_valid(form('invite_code'), db):
return(error(i18n.msg[6])) return(error(i18n.msg[6]))
except Exception as e:
LOG.error(e)
return(error(i18n.msg[6]))
if len(form('username')) < 3: if len(form('username')) < 3:
return error(i18n.msg[2]) return error(i18n.msg[2])
@@ -198,7 +262,7 @@ def post_signup():
return error(i18n.msg[15]) return error(i18n.msg[15])
if not tools.pwd_validation(form('password')): if not tools.pwd_validation(form('password')):
return error(i18n.msg[8]) #mezua ALDATU egin behar da return error(i18n.msg[8])
elif form('password') != form('confirm-password'): elif form('password') != form('confirm-password'):
return error(i18n.msg[7]) return error(i18n.msg[7])
@@ -276,33 +340,45 @@ def post_edit_email():
@post('/enable_2fa') @post('/enable_2fa')
def post_enable_2fa(): def post_enable_2fa():
def error(msg):
return _2fa_tpl(alerts=[('error', msg, 'fadeOut')], data=newSession().get(), str=i18n.str)
try:
if(not newSession().get()['secureAuth']):
try: try:
username=newSession().get()['username'] username=newSession().get()['username']
add_auth_attribute_step1(username, tools.generate_secret(), action='enable') add_auth_attribute_step1(username, tools.gen_secret(), action='enable')
'''
add attribute authCode
set session data
'''
except Error as e: except Error as e:
LOG.warning("akatsa") reload(newSession().get()['username'], None, None)
return error(str(e)) LOG.warning(e)
return error(i18n.msg[30])
except Error as e:
LOG.warning(e)
return index_tpl(alerts=[('error', e, 'fadeOut')], str=i18n.str)
return _2fa_tpl(data=newSession().get(), str=i18n.str) return _2fa_tpl(alerts=[('success', i18n.msg[31], 'fadeOut')], data=newSession().get(), str=i18n.str)
@post('/disable_2fa') @post('/disable_2fa')
def post_disable_2fa(): def post_disable_2fa():
def error(msg):
return _2fa_tpl(alerts=[('error', msg, 'fadeOut')], data=newSession().get(), str=i18n.str)
try:
if(newSession().get()['secureAuth']):
try: try:
username=newSession().get()['username'] username=newSession().get()['username']
add_auth_attribute_step1(username, tools.generate_secret(), action='disable') add_auth_attribute_step1(username, None, action='disable')
'''
add attribute authCode
set session data
'''
except Error as e: except Error as e:
LOG.warning("akatsa") reload(newSession().get()['username'], None, None)
LOG.warning(e)
return error(str(e)) return error(str(e))
except Error as e:
LOG.warning(e)
return index_tpl(alerts=[('error', e, 'fadeOut')], str=i18n.str)
return _2fa_tpl(data=newSession().get(), str=i18n.str) return _2fa_tpl(alerts=[('error', i18n.msg[32], 'fadeOut')], data=newSession().get(), str=i18n.str)
@post('/change_pwd') @post('/change_pwd')
def post_change_pwd(): def post_change_pwd():
@@ -362,10 +438,14 @@ def post_delete():
def serve_static(filename): def serve_static(filename):
return static_file(filename, root=path.join(BASE_DIR, 'static')) return static_file(filename, root=path.join(BASE_DIR, 'static'))
@get("/static/fonts/<filepath:re:.*\.(eot|otf|svg|ttf|woff|woff2?)>") @get("/static/fonts/<filepath:re:.*\\.(eot|otf|svg|ttf|woff|woff2?)>")
def font(filepath): def font(filepath):
return static_file(filepath, root="static/fonts") return static_file(filepath, root="static/fonts")
@get("/static/tmp/<filepath:re:.*\\.(png|svg)>")
def font(filepath):
return static_file(filepath, root="static/tmp")
def index_tpl(**kwargs): def index_tpl(**kwargs):
return template('index', **kwargs) return template('index', **kwargs)
@@ -401,8 +481,14 @@ def connect_ldap(conf, **kwargs):
return Connection(server, raise_exceptions=True, **kwargs) return Connection(server, raise_exceptions=True, **kwargs)
@error(404)
@error(405)
def error40x(error):
return index_tpl(str=i18n.str)
#LOGIN #LOGIN
def login(username, password): def login(username, password):
n = N n = N
for key in (key for key in CONF.sections() for key in (key for key in CONF.sections()
if key == 'ldap' or key.startswith('ldap:')): if key == 'ldap' or key.startswith('ldap:')):
@@ -417,7 +503,6 @@ def login(username, password):
continue continue
else: else:
raise e raise e
break break
def login_user(conf, *args): def login_user(conf, *args):
@@ -450,6 +535,9 @@ def login_user_ldap(conf, username, password):
#update timestamp + ip address #update timestamp + ip address
update_login_info(conf, user_dn) update_login_info(conf, user_dn)
LOG.debug("%s logged in to %s" % (username, conf['base'])) LOG.debug("%s logged in to %s" % (username, conf['base']))
#generate qr if it doenst exists when 2fa enable
if(newSession().get()['secureAuth']):
tools.gen_qr(newSession().get()['authCode'])
#LOGOUT #LOGOUT
def logout(username): def logout(username):
@@ -568,7 +656,8 @@ def register(conf, username, firstname, surname, password, email, isFake, device
firstname, 'sn': surname, 'uid' : username, 'mail': email, 'active': False, 'fakeCn': isFake, firstname, 'sn': surname, 'uid' : username, 'mail': email, 'active': False, 'fakeCn': isFake,
'devices':device, 'ip':request.environ.get('HTTP_X_REAL_IP', request.remote_addr), 'lastLogin': ts, 'devices':device, 'ip':request.environ.get('HTTP_X_REAL_IP', request.remote_addr), 'lastLogin': ts,
'secureAuth': False} 'secureAuth': False}
new_user_dn = "cn="+firstname+" "+surname+" - "+username+",cn=users,"+conf['base'] #new_user_dn = "cn="+firstname+" "+surname+" - "+username+",cn=users,"+conf['base']
new_user_dn = "cn="+firstname+" "+surname+",cn=users,"+conf['base']
c.add(dn=new_user_dn,object_class=OBJECT_CLASS, attributes=attributes) c.add(dn=new_user_dn,object_class=OBJECT_CLASS, attributes=attributes)
#create/change user password #create/change user password
c.extend.standard.modify_password(new_user_dn, '', password) c.extend.standard.modify_password(new_user_dn, '', password)
@@ -702,10 +791,10 @@ def add_auth_attribute_step1(username, code, action):
try: try:
add_auth_attribute_step2(CONF[key], username, code, action) add_auth_attribute_step2(CONF[key], username, code, action)
changed.append(key) changed.append(key)
LOG.debug("%s changed email address on %s" % (username, key)) LOG.debug("%s has activated 2FA authentication on %s" % (username, key))
except Error as e: except Error as e:
for key in reversed(changed): for key in reversed(changed):
LOG.info("Reverting email change in %s for %s" % (key, username)) LOG.info("Reverting 2FA activation in %s for %s due to errors" % (key, username))
try: try:
new_email_address(CONF[key], username, new_email, old_email) new_email_address(CONF[key], username, new_email, old_email)
except Error as e2: except Error as e2:
@@ -728,16 +817,18 @@ def add_auth_attribute_step2(conf, *args):
LOG.error('{}: {!s}'.format(e.__class__.__name__, e)) LOG.error('{}: {!s}'.format(e.__class__.__name__, e))
raise Error(i18n.msg[23]) raise Error(i18n.msg[23])
except LDAPNoSuchAttributeResult as e:
LOG.error('{}: {!s}'.format(e.__class__.__name__, e))
raise Error(i18n.msg[33])
except LDAPExceptionError as e: except LDAPExceptionError as e:
LOG.error('{}: {!s}'.format(e.__class__.__name__, e)) LOG.error('{}: {!s}'.format(e.__class__.__name__, e))
raise Error(i18n.msg[23]) raise Error(i18n.msg[23])
def add_auth_attribute_step3(conf, username, code, action): def add_auth_attribute_step3(conf, username, code, action):
#set current LDAP #set current LDAP
superUser = SuperUsers(conf) superUser = SuperUsers(conf)
print(action)
with connect_ldap(conf, user=superUser.admin_dn, password=superUser.admin_pwd) as c: with connect_ldap(conf, user=superUser.admin_dn, password=superUser.admin_pwd) as c:
user_dn = find_user_dn(conf, c, username) user_dn = find_user_dn(conf, c, username)
if(action == 'enable'): if(action == 'enable'):
@@ -746,8 +837,18 @@ def add_auth_attribute_step3(conf, username, code, action):
elif(action == 'disable'): elif(action == 'disable'):
c.modify(user_dn,{'authCode': [(MODIFY_DELETE, [])]}) c.modify(user_dn,{'authCode': [(MODIFY_DELETE, [])]})
c.modify(user_dn,{'secureAuth': [MODIFY_REPLACE, [False]]}) c.modify(user_dn,{'secureAuth': [MODIFY_REPLACE, [False]]})
#remove file
try:
remove('static/tmp/'+newSession().get()['authCode']+'.png')
except OSError as e:
LOG.warning(str(e))
#raise Error(e)
pass
newSession().set(get_user_data(user_dn, c)) newSession().set(get_user_data(user_dn, c))
reload=add_auth_attribute_step1
#CHANGE PASSWORD #CHANGE PASSWORD
def change_passwords(username, old_pass, new_pass): def change_passwords(username, old_pass, new_pass):
changed = [] changed = []
@@ -908,6 +1009,12 @@ def get_user_email_array(user_dn, conn, old_email, new_email):
emails[i] = new_email emails[i] = new_email
return(emails) return(emails)
def check_secure_auth(user_dn, conn):
search_filter = '(objectClass=*)'
conn.search(user_dn, search_filter, attributes=['secureAuth'])
status = conn.entries[0].secureAuth
return(status)
def get_user_data(user_dn, conn): def get_user_data(user_dn, conn):
search_filter = '(objectClass=*)' search_filter = '(objectClass=*)'
conn.search(user_dn, search_filter, conn.search(user_dn, search_filter,
@@ -921,11 +1028,17 @@ def get_user_data(user_dn, conn):
data.append(conn.entries[0].uid.values[0]) data.append(conn.entries[0].uid.values[0])
data.append(conn.entries[0].mail.values[0]) data.append(conn.entries[0].mail.values[0])
data.append(conn.entries[0].devices.values) data.append(conn.entries[0].devices.values)
if(conn.entries[0].ip):
data.append(conn.entries[0].ip.values[0]) data.append(conn.entries[0].ip.values[0])
else:
data.append(request.environ.get('HTTP_X_REAL_IP', request.remote_addr))
#ts = conn.entries[0].lastLogin.values[0] #ts = conn.entries[0].lastLogin.values[0]
#ts = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S%z') #ts = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S%z')
#ts = datetime.strftime(t, '%Y-%m-%d %H:%M:%S') #ts = datetime.strftime(t, '%Y-%m-%d %H:%M:%S')
if(conn.entries[0].lastLogin):
data.append(str(conn.entries[0].lastLogin.values[0])[:-6]) data.append(str(conn.entries[0].lastLogin.values[0])[:-6])
else:
data.append(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
data.append(conn.entries[0].secureAuth.values[0]) data.append(conn.entries[0].secureAuth.values[0])
if(conn.entries[0].authCode): if(conn.entries[0].authCode):
data.append(conn.entries[0].authCode.values[0]) data.append(conn.entries[0].authCode.values[0])
@@ -968,18 +1081,10 @@ def is_trusted_device(conf, user_dn):
if not find_device(user_dn, c, d): if not find_device(user_dn, c, d):
OBJECT_CLASS = ['top', 'inetOrgPerson', 'posixAccount', 'accountsManagement'] OBJECT_CLASS = ['top', 'inetOrgPerson', 'posixAccount', 'accountsManagement']
c.modify(user_dn, {'devices': [( MODIFY_ADD, d )] }) c.modify(user_dn, {'devices': [( MODIFY_ADD, d )] })
'''
if find_device(user_dn, c, 'unknown'):
OBJECT_CLASS = ['top', 'inetOrgPerson', 'posixAccount', 'accountsManagement']
c.modify(user_dn, {'devices': [( MODIFY_REPLACE, d )] })
else:
OBJECT_CLASS = ['top', 'inetOrgPerson', 'posixAccount', 'accountsManagement']
c.modify(user_dn, {'devices': [( MODIFY_ADD, d )] })
'''
c.unbind() c.unbind()
return True return True
except Exception as e: except Exception as e:
print(e) LOG.warning(e)
return True return True
def update_login_info(conf, user_dn): def update_login_info(conf, user_dn):
@@ -993,6 +1098,14 @@ def update_login_info(conf, user_dn):
class Error(Exception): class Error(Exception):
pass pass
# TEMPORAL MEMORY
class tMemory(object):
def __init__(self):
self.data = None
self.key = None
memo = tMemory()
#SESSIONS MANAGEMENT #SESSIONS MANAGEMENT
def newSession(): def newSession():
@@ -1001,7 +1114,7 @@ def newSession():
def __init__(self): def __init__(self):
super(Session, self).__init__() super(Session, self).__init__()
self.data = bottle.request.environ.get('beaker.session') self.data = bottle.request.environ.get('beaker.session')
self.lang = self.get_lang() self.sid = self.data.id
#localization #localization
self.lang = self.get_lang() self.lang = self.get_lang()
global i18n global i18n
@@ -1048,6 +1161,8 @@ def newSession():
self.data['secureAuth'] = self.secureAuth self.data['secureAuth'] = self.secureAuth
self.data['authCode'] = self.authCode self.data['authCode'] = self.authCode
self.data['id'] = self.sid
def close(self): def close(self):
self.data.pop('username') self.data.pop('username')
@@ -1084,7 +1199,8 @@ class SuperUsers(object):
superUser = SuperUsers(CONF['ldap:0']) superUser = SuperUsers(CONF['ldap:0'])
app = beaker.middleware.SessionMiddleware(bottle.app(), session_opts) #app = beaker.middleware.SessionMiddleware(bottle.app(), session_opts)
app = SessionMiddleware(bottle.app(), session_opts)
bottle.TEMPLATE_PATH = [BASE_DIR] bottle.TEMPLATE_PATH = [BASE_DIR]
Binary file not shown.
+9
View File
@@ -15,6 +15,13 @@
<main> <main>
<h1>{{ str['login'] }}</h1> <h1>{{ str['login'] }}</h1>
%try:
%if two_factor_authentication:
<form method="post" action="/user/{{path}}">
<label for="token">{{ str['token'] }}</label>
<input id="token" name="token" value="" type="text" required autofocus>
%end
%except:
<form method="post" action="/user"> <form method="post" action="/user">
<label for="username">{{ str['usrn'] }}</label> <label for="username">{{ str['usrn'] }}</label>
<input id="username" name="username" value="{{ get('username', '') }}" type="text" required autofocus> <input id="username" name="username" value="{{ get('username', '') }}" type="text" required autofocus>
@@ -22,6 +29,8 @@
<label for="password">{{ str['pwd'] }}</label> <label for="password">{{ str['pwd'] }}</label>
<input id="password" name="password" type="password" pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*_=+-]).{8,24}$" oninvalid="setCustomValidity('{{ str['pwd-pattern'] }}')" oninput="setCustomValidity('')" required> <input id="password" name="password" type="password" pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*_=+-]).{8,24}$" oninvalid="setCustomValidity('{{ str['pwd-pattern'] }}')" oninput="setCustomValidity('')" required>
%end
<button class="green" type="submit">{{str['login']}}</button> <button class="green" type="submit">{{str['login']}}</button>
<a href="/signup">{{ str['or-sign-up'] }}</a> <a href="/signup">{{ str['or-sign-up'] }}</a>
</form> </form>
-29
View File
@@ -1,29 +0,0 @@
from onetimepass import valid_totp
from secrets import choice
def generate_secret(): # Function to return a random string with length 16.
secret = ''
while len(secret) < 16:
secret += choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
return secret
secret = generate_secret()
print('Enter the following secret in your authenticator app: ', secret)
print("""
Instructions for saving this secret it Google Authenticator:
1. Open Google Authenticator.
2. Click plus icon at the right bottom.
3. Click Enter a setup key.
4. Enter an Account name of your choice and enter the secret provided above.
5. Click Add.
""")
while True:
otp = int(input('Please enter the otp generated by your authenticator app: '))
authenticated = valid_totp(otp, secret)
if authenticated:
print('Correct otp, Authenticated!')
elif not authenticated:
print('Wrong otp, please try again.')
+22 -1
View File
@@ -4,6 +4,9 @@ import sqlite3
import re import re
from onetimepass import valid_totp from onetimepass import valid_totp
from secrets import choice from secrets import choice
import segno
from os import path
import uuid
class Tools(): class Tools():
@@ -45,11 +48,29 @@ class Tools():
regex = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!#%*?&]{8,18}$' regex = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!#%*?&]{8,18}$'
return(bool(re.fullmatch(regex, e))) return(bool(re.fullmatch(regex, e)))
def generate_secret(self): # Function to return a random string with length 16. # 2FA
def gen_qr(self, secret):
if(not path.isfile('static/tmp/'+secret+'.png')):
qrcode = segno.make(secret, micro=False)
qrcode.save('static/tmp/'+secret+'.png', scale=10)
def gen_secret(self): # Function to return a random string with length 16.
secret = '' secret = ''
while len(secret) < 16: while len(secret) < 16:
secret += choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567') secret += choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
self.gen_qr(secret)
return secret return secret
def _2fa_validation(self, otp, secret):
authenticated = valid_totp(otp, secret)
if authenticated:
print('Correct otp, Authenticated!')
return True
elif not authenticated:
print('Wrong otp, please try again.')
return False
def key(self):
return uuid.uuid4().hex
tools = Tools() tools = Tools()
+17 -2
View File
@@ -40,6 +40,13 @@ class LocalizeTo(object):
str_25 = _("Last login") str_25 = _("Last login")
str_26 = _("Devices") str_26 = _("Devices")
str_27 = _("show") str_27 = _("show")
str_28 = _("Two factor authentication")
str_29 = _("Token")
str_30 = _("Disable")
str_31 = _("Enable")
str_32 = _("You must then use the key created by this QR code to log in. To get the key you must use a OTP or 2FA mobile app.")
str_33 = _("Two-step authentication is additional security. By enabling this you will be asked at the login, in addition to the user and password, a key you will need to create using QR code.")
str_34 = _("Code")
#messages #messages
msg_00 = _("The session was closed.") msg_00 = _("The session was closed.")
@@ -71,11 +78,19 @@ class LocalizeTo(object):
msg_26 = _("Forgot your password? Please try again.") msg_26 = _("Forgot your password? Please try again.")
msg_27 = _("The session has expired.") msg_27 = _("The session has expired.")
msg_28 = _("Registration is currently closed. We apologize for the inconvenience.") msg_28 = _("Registration is currently closed. We apologize for the inconvenience.")
msg_29 = _("Two factor authentication has been impossible.")
msg_30 = _("Two factor authentication has been restored.")
msg_31 = _("Two factor authentication has been enabled.")
msg_32 = _("Two factor authentication has been disabled.")
msg_33 = _("Two factor authentication was already disabled.")
self.str = {'usr':str_00, 'usrn':str_01, 'fn':str_02, 'sn':str_03, 'pwd':str_04, 'old-pwd':str_05, self.str = {'usr':str_00, 'usrn':str_01, 'fn':str_02, 'sn':str_03, 'pwd':str_04, 'old-pwd':str_05,
'new-pwd':str_06, 'conf-pwd':str_07, 'email':str_08, 'edit':str_09, 'login':str_10, 'log-out':str_11, 'new-pwd':str_06, 'conf-pwd':str_07, 'email':str_08, 'edit':str_09, 'login':str_10, 'log-out':str_11,
'del':str_12, 'sign-up':str_13, 'back':str_14, 'update':str_15, 'or-sign-in':str_16, 'del':str_12, 'sign-up':str_13, 'back':str_14, 'update':str_15, 'or-sign-in':str_16,
'or-sign-up':str_17, 'inv-code':str_18, 'edit-fn':str_19, 'edit-email':str_20, 'ch-pwd':str_21, 'or-sign-up':str_17, 'inv-code':str_18, 'edit-fn':str_19, 'edit-email':str_20, 'ch-pwd':str_21,
'del-account':str_22, 'welcome':str_23, 'logs':str_24, 'last-login':str_25, 'devices':str_26, 'del-account':str_22, 'welcome':str_23, 'logs':str_24, 'last-login':str_25, 'devices':str_26,
'show':str_27, 'pwd-pattern': msg_08} 'show':str_27, '2fa-title':str_28, 'token':str_29, 'disable':str_30, 'enable':str_31, '2fa-info-1':str_32,
self.msg = (msg_00, msg_01, msg_02, msg_03, msg_04, msg_05, msg_06, msg_07, msg_08, msg_09, msg_10, msg_11, msg_12, msg_13, msg_14, msg_15, msg_16, msg_17, msg_18, msg_19, msg_20, msg_21, msg_22, msg_23, msg_24, msg_25, msg_26, msg_27, msg_28) '2fa-info-2':str_33, 'code':str_34, 'pwd-pattern': msg_08}
self.msg = (msg_00, msg_01, msg_02, msg_03, msg_04, msg_05, msg_06, msg_07, msg_08, msg_09, msg_10, msg_11,
msg_12, msg_13, msg_14, msg_15, msg_16, msg_17, msg_18, msg_19, msg_20, msg_21, msg_22, msg_23,
msg_24, msg_25, msg_26, msg_27, msg_28, msg_29, msg_30, msg_31, msg_32, msg_33)
+140 -98
View File
@@ -6,243 +6,285 @@
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: 0.0.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-25 16:52+0200\n" "POT-Creation-Date: 2023-11-27 15:52+0100\n"
"PO-Revision-Date: 2023-04-07 13:28+0200\n" "PO-Revision-Date: 2023-04-07 13:28+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.2.2\n"
#: libs/localization.py:15 #: libs/localization.py:15
msgid "User" msgid "User"
msgstr "User" msgstr ""
#: libs/localization.py:16 #: libs/localization.py:16
msgid "Username" msgid "Username"
msgstr "Username" msgstr ""
#: libs/localization.py:17 #: libs/localization.py:17
msgid "Firstname" msgid "Firstname"
msgstr "Firstname" msgstr ""
#: libs/localization.py:18 #: libs/localization.py:18
msgid "Surname" msgid "Surname"
msgstr "Surname" msgstr ""
#: libs/localization.py:19 #: libs/localization.py:19
msgid "Password" msgid "Password"
msgstr "Password" msgstr ""
#: libs/localization.py:20 #: libs/localization.py:20
msgid "Old password" msgid "Old password"
msgstr "Old password" msgstr ""
#: libs/localization.py:21 #: libs/localization.py:21
msgid "New password" msgid "New password"
msgstr "New password" msgstr ""
#: libs/localization.py:22 #: libs/localization.py:22
msgid "Confirm password" msgid "Confirm password"
msgstr "Confirm password" msgstr ""
#: libs/localization.py:23 #: libs/localization.py:23
msgid "Email" msgid "Email"
msgstr "Email" msgstr ""
#: libs/localization.py:24 #: libs/localization.py:24
msgid "edit" msgid "edit"
msgstr "edit" msgstr ""
#: libs/localization.py:25 #: libs/localization.py:25
msgid "Login" msgid "Login"
msgstr "Login" msgstr ""
#: libs/localization.py:26 #: libs/localization.py:26
msgid "Logout" msgid "Logout"
msgstr "Logout" msgstr ""
#: libs/localization.py:27 #: libs/localization.py:27
msgid "Delete" msgid "Delete"
msgstr "Delete" msgstr ""
#: libs/localization.py:28 #: libs/localization.py:28
msgid "Sign Up" msgid "Sign Up"
msgstr "Sign Up" msgstr ""
#: libs/localization.py:29 #: libs/localization.py:29
msgid "Back" msgid "Back"
msgstr "Back" msgstr ""
#: libs/localization.py:30 #: libs/localization.py:30
msgid "Update" msgid "Update"
msgstr "Update" msgstr ""
#: libs/localization.py:31 #: libs/localization.py:31
msgid "Or Sign In" msgid "Or Sign In"
msgstr "Or Sign In" msgstr ""
#: libs/localization.py:32 #: libs/localization.py:32
msgid "Or Sign Up" msgid "Or Sign Up"
msgstr "Or Sign Up" msgstr ""
#: libs/localization.py:33 #: libs/localization.py:33
msgid "Invite code" msgid "Invite code"
msgstr "Invite code" msgstr ""
#: libs/localization.py:34 #: libs/localization.py:34
msgid "Edit your fullname" msgid "Edit your fullname"
msgstr "Edit your fullname" msgstr ""
#: libs/localization.py:35 #: libs/localization.py:35
msgid "Edit your email" msgid "Edit your email"
msgstr "Edit your email" msgstr ""
#: libs/localization.py:36 #: libs/localization.py:36
msgid "Change your password" msgid "Change your password"
msgstr "Change your password" msgstr ""
#: libs/localization.py:37 #: libs/localization.py:37
msgid "Delete your account" msgid "Delete your account"
msgstr "Delete your account" msgstr ""
#: libs/localization.py:38 libs/localization.py:46 #: libs/localization.py:38 libs/localization.py:53
msgid "Welcome" msgid "Welcome"
msgstr "Welcome" msgstr ""
#: libs/localization.py:39 #: libs/localization.py:39
msgid "Logs" msgid "Logs"
msgstr "Logs" msgstr ""
#: libs/localization.py:40 #: libs/localization.py:40
msgid "Last login" msgid "Last login"
msgstr "Last login" msgstr ""
#: libs/localization.py:41 #: libs/localization.py:41
msgid "Devices" msgid "Devices"
msgstr "Devices" msgstr ""
#: libs/localization.py:42 #: libs/localization.py:42
msgid "show" msgid "show"
msgstr "show" msgstr ""
#: libs/localization.py:43
msgid "Two factor authentication"
msgstr ""
#: libs/localization.py:44
msgid "Token"
msgstr ""
#: libs/localization.py:45 #: libs/localization.py:45
msgid "The session was closed." msgid "Disable"
msgstr "The session was closed." msgstr ""
#: libs/localization.py:46
msgid "Enable"
msgstr ""
#: libs/localization.py:47 #: libs/localization.py:47
msgid "Username must be at least 3 characters long!" msgid "You must then use the key created by this QR code to log in. To get the key you must use a OTP or 2FA mobile app."
msgstr "Username must be at least 3 characters long!" msgstr ""
#: libs/localization.py:48 #: libs/localization.py:48
msgid "Not allowed characters for the username field." msgid "Two-step authentication is additional security. By enabling this you will be asked at the login, in addition to the user and password, a key you will need to create using QR code."
msgstr "Not allowed characters for the username field." msgstr ""
#: libs/localization.py:49 #: libs/localization.py:49
msgid "Not allowed characters for the firstname field." msgid "Code"
msgstr "Not allowed characters for the firstname field." msgstr ""
#: libs/localization.py:50
msgid "Not allowed characters for the surname field."
msgstr "Not allowed characters for the surname field."
#: libs/localization.py:51
msgid "The code is invalid or has expired."
msgstr "The code is invalid or has expired."
#: libs/localization.py:52 #: libs/localization.py:52
msgid "Passwords do not match!" msgid "The session was closed."
msgstr "Passwords do not match!"
#: libs/localization.py:53
msgid ""
"The password must contain at least 8 characters, at least one number, "
"a capital letter and a special character."
msgstr "" msgstr ""
"The password must contain at least 8 characters, at least one number, "
"a capital letter and a special character."
#: libs/localization.py:54 #: libs/localization.py:54
msgid "Congratulations, your account has been created!" msgid "Username must be at least 3 characters long!"
msgstr "Congratulations, your account has been created!" msgstr ""
#: libs/localization.py:55 #: libs/localization.py:55
msgid "Your first and last name have not been changed." msgid "Not allowed characters for the username field."
msgstr "Your first and last name have not been changed." msgstr ""
#: libs/localization.py:56 #: libs/localization.py:56
msgid "Your firstname is a bit short, don't you think?" msgid "Not allowed characters for the firstname field."
msgstr "Your firstname is a bit short, don't you think?" msgstr ""
#: libs/localization.py:57 #: libs/localization.py:57
msgid "Your surname is a bit short, don't you think?" msgid "Not allowed characters for the surname field."
msgstr "Your surname is a bit short, don't you think?" msgstr ""
#: libs/localization.py:58 #: libs/localization.py:58
msgid "Your first and last name have been successfully updated." msgid "The code is invalid or has expired."
msgstr "Your first and last name have been successfully updated." msgstr ""
#: libs/localization.py:59 #: libs/localization.py:59
msgid "Invalid email address. Please try again." msgid "Passwords do not match!"
msgstr "Invalid email address. Please try again." msgstr ""
#: libs/localization.py:60 #: libs/localization.py:60
msgid "Email address has not been changed." msgid "The password must contain at least 8 characters, at least one number, a capital letter and a special character."
msgstr "Email address has not been changed." msgstr ""
#: libs/localization.py:61 #: libs/localization.py:61
msgid "Your email has been successfully updated." msgid "Congratulations, your account has been created!"
msgstr "Your email has been successfully updated." msgstr ""
#: libs/localization.py:62 #: libs/localization.py:62
msgid "The password entered is the same as the current password." msgid "Your first and last name have not been changed."
msgstr "The password entered is the same as the current password." msgstr ""
#: libs/localization.py:63 #: libs/localization.py:63
msgid "Password has been changed!" msgid "Your firstname is a bit short, don't you think?"
msgstr "Password has been changed!" msgstr ""
#: libs/localization.py:64 #: libs/localization.py:64
msgid "Please, type your username for account deletion." msgid "Your surname is a bit short, don't you think?"
msgstr "Please, type your username for account deletion." msgstr ""
#: libs/localization.py:65 #: libs/localization.py:65
msgid "Account successfully deleted!" msgid "Your first and last name have been successfully updated."
msgstr "Account successfully deleted!" msgstr ""
#: libs/localization.py:66 #: libs/localization.py:66
msgid "Username or password is incorrect!" msgid "Invalid email address. Please try again."
msgstr "Username or password is incorrect!" msgstr ""
#: libs/localization.py:67 #: libs/localization.py:67
msgid "Unable to connect to the remote server." msgid "Email address has not been changed."
msgstr "Unable to connect to the remote server." msgstr ""
#: libs/localization.py:68 #: libs/localization.py:68
msgid "" msgid "Your email has been successfully updated."
"Encountered an unexpected error while communicating with the remote server."
msgstr "" msgstr ""
"Encountered an unexpected error while communicating with the remote server."
#: libs/localization.py:69 #: libs/localization.py:69
msgid "User already exists." msgid "The password entered is the same as the current password."
msgstr "User already exists." msgstr ""
#: libs/localization.py:70 #: libs/localization.py:70
msgid "Email already exists." msgid "Password has been changed!"
msgstr "Email already exists." msgstr ""
#: libs/localization.py:71 #: libs/localization.py:71
msgid "Forgot your password? Please try again." msgid "Please, type your username for account deletion."
msgstr "Forgot your password? Please try again." msgstr ""
#: libs/localization.py:72 #: libs/localization.py:72
msgid "The session has expired." msgid "Account successfully deleted!"
msgstr "The session has expired." msgstr ""
#: libs/localization.py:73 #: libs/localization.py:73
msgid "Username or password is incorrect!"
msgstr ""
#: libs/localization.py:74
msgid "Unable to connect to the remote server."
msgstr ""
#: libs/localization.py:75
msgid "Encountered an unexpected error while communicating with the remote server."
msgstr ""
#: libs/localization.py:76
msgid "User already exists."
msgstr ""
#: libs/localization.py:77
msgid "Email already exists."
msgstr ""
#: libs/localization.py:78
msgid "Forgot your password? Please try again."
msgstr ""
#: libs/localization.py:79
msgid "The session has expired."
msgstr ""
#: libs/localization.py:80
msgid "Registration is currently closed. We apologize for the inconvenience." msgid "Registration is currently closed. We apologize for the inconvenience."
msgstr "Registration is currently closed. We apologize for the inconvenience." msgstr ""
#: libs/localization.py:81
msgid "Two factor authentication has been impossible."
msgstr ""
#: libs/localization.py:82
msgid "Two factor authentication has been restored."
msgstr ""
#: libs/localization.py:83
msgid "Two factor authentication has been enabled."
msgstr ""
#: libs/localization.py:84
msgid "Two factor authentication has been disabled."
msgstr ""
#: libs/localization.py:85
msgid "Two factor authentication was already disabled."
msgstr ""
Binary file not shown.
+87 -45
View File
@@ -1,21 +1,21 @@
# SOME DESCRIPTIVE TITLE. # SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <aitzol@lainoa.eus>, 2022. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.0.1\n" "Project-Id-Version: 0.0.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-07 17:20+0200\n" "POT-Creation-Date: 2022-04-25 16:52+0200\n"
"PO-Revision-Date: 2023-04-07 13:28+0200\n" "PO-Revision-Date: 2023-11-27 15:37+0100\n"
"Last-Translator: Aitzol Berasategi <aitzol@lainoa.eus>\n" "Last-Translator: \n"
"Language-Team: LANGUAGE <EN@en.org>\n" "Language-Team: \n"
"Language: en\n" "Language: en\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.3\n" "X-Generator: Poedit 3.2.2\n"
#: libs/localization.py:15 #: libs/localization.py:15
msgid "User" msgid "User"
@@ -109,7 +109,7 @@ msgstr "Change your password"
msgid "Delete your account" msgid "Delete your account"
msgstr "Delete your account" msgstr "Delete your account"
#: libs/localization.py:38 libs/localization.py:46 #: libs/localization.py:38 libs/localization.py:53
msgid "Welcome" msgid "Welcome"
msgstr "Welcome" msgstr "Welcome"
@@ -129,120 +129,162 @@ msgstr "Devices"
msgid "show" msgid "show"
msgstr "show" msgstr "show"
#: libs/localization.py:43
msgid "Two factor authentication"
msgstr "Two factor authentication"
#: libs/localization.py:44
msgid "Token"
msgstr "Token"
#: libs/localization.py:45 #: libs/localization.py:45
msgid "Disable"
msgstr "Disable"
#: libs/localization.py:46
msgid "Enable"
msgstr "Enable"
#: libs/localization.py:47
msgid "You must then use the key created by this QR code to log in. To get the key you must use a OTP or 2FA mobile app."
msgstr "You must then use the key created by this QR code to log in. To get the key you must use a OTP or 2FA mobile app."
#: libs/localization.py:48
msgid "Two-step authentication is additional security. By enabling this you will be asked at the login, in addition to the user and password, a key you will need to create using QR code."
msgstr "Two-step authentication is additional security. By enabling this you will be asked at the login, in addition to the user and password, a key you will need to create using QR code."
#: libs/localization.py:49
msgid "Code"
msgstr "Code"
#: libs/localization.py:52
msgid "The session was closed." msgid "The session was closed."
msgstr "The session was closed." msgstr "The session was closed."
#: libs/localization.py:47 #: libs/localization.py:54
msgid "Username must be at least 3 characters long!" msgid "Username must be at least 3 characters long!"
msgstr "Username must be at least 3 characters long!" msgstr "Username must be at least 3 characters long!"
#: libs/localization.py:48 #: libs/localization.py:55
msgid "Not allowed characters for the username field." msgid "Not allowed characters for the username field."
msgstr "Not allowed characters for the username field." msgstr "Not allowed characters for the username field."
#: libs/localization.py:49 #: libs/localization.py:56
msgid "Not allowed characters for the firstname field." msgid "Not allowed characters for the firstname field."
msgstr "Not allowed characters for the firstname field." msgstr "Not allowed characters for the firstname field."
#: libs/localization.py:50 #: libs/localization.py:57
msgid "Not allowed characters for the surname field." msgid "Not allowed characters for the surname field."
msgstr "Not allowed characters for the surname field." msgstr "Not allowed characters for the surname field."
#: libs/localization.py:51 #: libs/localization.py:58
msgid "The code is invalid or has expired." msgid "The code is invalid or has expired."
msgstr "The code is invalid or has expired." msgstr "The code is invalid or has expired."
#: libs/localization.py:52 #: libs/localization.py:59
msgid "Passwords do not match!" msgid "Passwords do not match!"
msgstr "Passwords do not match!" msgstr "Passwords do not match!"
#: libs/localization.py:53 #: libs/localization.py:60
msgid "" msgid "The password must contain at least 8 characters, at least one number, a capital letter and a special character."
"The password must contain at least 8 characters, at least one number, " msgstr "The password must contain at least 8 characters, at least one number, a capital letter and a special character."
"a capital letter and a special character."
msgstr ""
"The password must contain at least 8 characters, at least one number, "
"a capital letter and a special character."
#: libs/localization.py:54 #: libs/localization.py:61
msgid "Congratulations, your account has been created!" msgid "Congratulations, your account has been created!"
msgstr "Congratulations, your account has been created!" msgstr "Congratulations, your account has been created!"
#: libs/localization.py:55 #: libs/localization.py:62
msgid "Your first and last name have not been changed." msgid "Your first and last name have not been changed."
msgstr "Your first and last name have not been changed." msgstr "Your first and last name have not been changed."
#: libs/localization.py:56 #: libs/localization.py:63
msgid "Your firstname is a bit short, don't you think?" msgid "Your firstname is a bit short, don't you think?"
msgstr "Your firstname is a bit short, don't you think?" msgstr "Your firstname is a bit short, don't you think?"
#: libs/localization.py:57 #: libs/localization.py:64
msgid "Your surname is a bit short, don't you think?" msgid "Your surname is a bit short, don't you think?"
msgstr "Your surname is a bit short, don't you think?" msgstr "Your surname is a bit short, don't you think?"
#: libs/localization.py:58 #: libs/localization.py:65
msgid "Your first and last name have been successfully updated." msgid "Your first and last name have been successfully updated."
msgstr "Your first and last name have been successfully updated." msgstr "Your first and last name have been successfully updated."
#: libs/localization.py:59 #: libs/localization.py:66
msgid "Invalid email address. Please try again." msgid "Invalid email address. Please try again."
msgstr "Invalid email address. Please try again." msgstr "Invalid email address. Please try again."
#: libs/localization.py:60 #: libs/localization.py:67
msgid "Email address has not been changed." msgid "Email address has not been changed."
msgstr "Email address has not been changed." msgstr "Email address has not been changed."
#: libs/localization.py:61 #: libs/localization.py:68
msgid "Your email has been successfully updated." msgid "Your email has been successfully updated."
msgstr "Your email has been successfully updated." msgstr "Your email has been successfully updated."
#: libs/localization.py:62 #: libs/localization.py:69
msgid "The password entered is the same as the current password." msgid "The password entered is the same as the current password."
msgstr "The password entered is the same as the current password." msgstr "The password entered is the same as the current password."
#: libs/localization.py:63 #: libs/localization.py:70
msgid "Password has been changed!" msgid "Password has been changed!"
msgstr "Password has been changed!" msgstr "Password has been changed!"
#: libs/localization.py:64 #: libs/localization.py:71
msgid "Please, type your username for account deletion." msgid "Please, type your username for account deletion."
msgstr "Please, type your username for account deletion." msgstr "Please, type your username for account deletion."
#: libs/localization.py:65 #: libs/localization.py:72
msgid "Account successfully deleted!" msgid "Account successfully deleted!"
msgstr "Account successfully deleted!" msgstr "Account successfully deleted!"
#: libs/localization.py:66 #: libs/localization.py:73
msgid "Username or password is incorrect!" msgid "Username or password is incorrect!"
msgstr "Username or password is incorrect!" msgstr "Username or password is incorrect!"
#: libs/localization.py:67 #: libs/localization.py:74
msgid "Unable to connect to the remote server." msgid "Unable to connect to the remote server."
msgstr "Unable to connect to the remote server." msgstr "Unable to connect to the remote server."
#: libs/localization.py:68 #: libs/localization.py:75
msgid "" msgid "Encountered an unexpected error while communicating with the remote server."
"Encountered an unexpected error while communicating with the remote server." msgstr "Encountered an unexpected error while communicating with the remote server."
msgstr ""
"Encountered an unexpected error while communicating with the remote server."
#: libs/localization.py:69 #: libs/localization.py:76
msgid "User already exists." msgid "User already exists."
msgstr "User already exists." msgstr "User already exists."
#: libs/localization.py:70 #: libs/localization.py:77
msgid "Email already exists." msgid "Email already exists."
msgstr "Email already exists." msgstr "Email already exists."
#: libs/localization.py:71 #: libs/localization.py:78
msgid "Forgot your password? Please try again." msgid "Forgot your password? Please try again."
msgstr "Forgot your password? Please try again." msgstr "Forgot your password? Please try again."
#: libs/localization.py:72 #: libs/localization.py:79
msgid "The session has expired." msgid "The session has expired."
msgstr "The session has expired." msgstr "The session has expired."
#: libs/localization.py:73 #: libs/localization.py:80
msgid "Registration is currently closed. We apologize for the inconvenience." msgid "Registration is currently closed. We apologize for the inconvenience."
msgstr "Registration is currently closed. We apologize for the inconvenience." msgstr "Registration is currently closed. We apologize for the inconvenience."
#: libs/localization.py:81
msgid "Two factor authentication has been impossible."
msgstr "Two factor authentication has been impossible."
#: libs/localization.py:82
msgid "Two factor authentication has been restored."
msgstr "Two factor authentication has been restored."
#: libs/localization.py:83
msgid "Two factor authentication has been enabled."
msgstr "Two factor authentication has been enabled."
#: libs/localization.py:84
msgid "Two factor authentication has been disabled."
msgstr "Two factor authentication has been disabled."
#: libs/localization.py:85
msgid "Two factor authentication was already disabled."
msgstr "Two factor authentication was already disabled."
Binary file not shown.
+82 -39
View File
@@ -5,17 +5,17 @@
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 0.0.1\n" "Project-Id-Version: 0.0.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-04-07 17:23+0200\n" "POT-Creation-Date: 2022-04-07 17:23+0200\n"
"PO-Revision-Date: 2023-04-07 13:27+0200\n" "PO-Revision-Date: 2023-11-27 15:36+0100\n"
"Last-Translator: Aitzol Berasategi <aitzol@lainoa.eus>\n" "Last-Translator: Aitzol Berasategi <aitzol@lainoa.eus>\n"
"Language-Team: LANGUAGE <EU@eu.org>\n" "Language-Team: LANGUAGE <EU@eu.org>\n"
"Language: eu\n" "Language: eu\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.3\n" "X-Generator: Poedit 3.2.2\n"
#: libs/localization.py:15 #: libs/localization.py:15
msgid "User" msgid "User"
@@ -109,7 +109,7 @@ msgstr "Pasahitza eguneratu"
msgid "Delete your account" msgid "Delete your account"
msgstr "Kontua ezabatu" msgstr "Kontua ezabatu"
#: libs/localization.py:38 libs/localization.py:46 #: libs/localization.py:38 libs/localization.py:53
msgid "Welcome" msgid "Welcome"
msgstr "Ongi etorri" msgstr "Ongi etorri"
@@ -129,119 +129,162 @@ msgstr "Gailuak"
msgid "show" msgid "show"
msgstr "ikusi" msgstr "ikusi"
#: libs/localization.py:43
msgid "Two factor authentication"
msgstr "Bi urratseko autentifikazioa"
#: libs/localization.py:44
msgid "Token"
msgstr "Gakoa"
#: libs/localization.py:45 #: libs/localization.py:45
msgid "Disable"
msgstr "Desgaitu"
#: libs/localization.py:46
msgid "Enable"
msgstr "Gaitu"
#: libs/localization.py:47
msgid "You must then use the key created by this QR code to log in. To get the key you must use a OTP or 2FA mobile app."
msgstr "Aurrerantzean QR kode honen bidez sortutako gakoa erabili beharko duzu saioa hasteko. Gakoa lortzeko mugikorrerako OTP edo 2FA aplikazio bat erabili beharko duzu."
#: libs/localization.py:48
msgid "Two-step authentication is additional security. By enabling this you will be asked at the login, in addition to the user and password, a key you will need to create using QR code."
msgstr "Bi urratseko autentifikazioa segurtasun gehigarri bat da. Hau gaituz saio hasieran erabiltzaile eta pasahitzaz gain QR kode bidez sortu beharko duzun gako bat eskatuko zaizu."
#: libs/localization.py:49
msgid "Code"
msgstr "Kodea"
#: libs/localization.py:52
msgid "The session was closed." msgid "The session was closed."
msgstr "Saioa itxi da." msgstr "Saioa itxi da."
#: libs/localization.py:47 #: libs/localization.py:54
msgid "Username must be at least 3 characters long!" msgid "Username must be at least 3 characters long!"
msgstr "Erabiltzaile izenak gutxienez 3 karaktere izan behar ditu!" msgstr "Erabiltzaile izenak gutxienez 3 karaktere izan behar ditu!"
#: libs/localization.py:48 #: libs/localization.py:55
msgid "Not allowed characters for the username field." msgid "Not allowed characters for the username field."
msgstr "Erabiltzaile-izenaren eremurako onartzen ez diren karaktereak." msgstr "Erabiltzaile-izenaren eremurako onartzen ez diren karaktereak."
#: libs/localization.py:49 #: libs/localization.py:56
msgid "Not allowed characters for the firstname field." msgid "Not allowed characters for the firstname field."
msgstr "Izenaren eremurako onartzen ez diren karaktereak." msgstr "Izenaren eremurako onartzen ez diren karaktereak."
#: libs/localization.py:50 #: libs/localization.py:57
msgid "Not allowed characters for the surname field." msgid "Not allowed characters for the surname field."
msgstr "Abizenaren eremurako onartzen ez diren karaktereak." msgstr "Abizenaren eremurako onartzen ez diren karaktereak."
#: libs/localization.py:51 #: libs/localization.py:58
msgid "The code is invalid or has expired." msgid "The code is invalid or has expired."
msgstr "Kodea baliogabea da edo iraungi egin da." msgstr "Kodea baliogabea da edo iraungi egin da."
#: libs/localization.py:52 #: libs/localization.py:59
msgid "Passwords do not match!" msgid "Passwords do not match!"
msgstr "Pasahitzak ez datoz bat!" msgstr "Pasahitzak ez datoz bat!"
#: libs/localization.py:53 #: libs/localization.py:60
msgid "" msgid "The password must contain at least 8 characters, at least one number, a capital letter and a special character."
"The password must contain at least 8 characters, at least one number, " msgstr "Pasahitzak gutxienez 8 karaktere izan behar ditu, zenbaki bat, hizki larri bat eta karaktere berezi bat."
"a capital letter and a special character."
msgstr ""
"Pasahitzak gutxienez 8 karaktere izan behar ditu, zenbaki bat, hizki larri bat "
"eta karaktere berezi bat."
#: libs/localization.py:54 #: libs/localization.py:61
msgid "Congratulations, your account has been created!" msgid "Congratulations, your account has been created!"
msgstr "Zorionak, zure kontua sortu da!" msgstr "Zorionak, zure kontua sortu da!"
#: libs/localization.py:55 #: libs/localization.py:62
msgid "Your first and last name have not been changed." msgid "Your first and last name have not been changed."
msgstr "Zure izen-abizenak ez dira aldatu." msgstr "Zure izen-abizenak ez dira aldatu."
#: libs/localization.py:56 #: libs/localization.py:63
msgid "Your firstname is a bit short, don't you think?" msgid "Your firstname is a bit short, don't you think?"
msgstr "Zure izena labur-xamarra da, ez duzu uste?" msgstr "Zure izena labur-xamarra da, ez duzu uste?"
#: libs/localization.py:57 #: libs/localization.py:64
msgid "Your surname is a bit short, don't you think?" msgid "Your surname is a bit short, don't you think?"
msgstr "Zure abizena labur-xamarra da, ez duzu uste?" msgstr "Zure abizena labur-xamarra da, ez duzu uste?"
#: libs/localization.py:58 #: libs/localization.py:65
msgid "Your first and last name have been successfully updated." msgid "Your first and last name have been successfully updated."
msgstr "Zure izen-abizenak ongi eguneratu dira." msgstr "Zure izen-abizenak ongi eguneratu dira."
#: libs/localization.py:59 #: libs/localization.py:66
msgid "Invalid email address. Please try again." msgid "Invalid email address. Please try again."
msgstr "Baliogabeko email helbidea. Saia zaitez berriz, mesedez." msgstr "Baliogabeko email helbidea. Saia zaitez berriz, mesedez."
#: libs/localization.py:60 #: libs/localization.py:67
msgid "Email address has not been changed." msgid "Email address has not been changed."
msgstr "Email helbidea ez da aldatu." msgstr "Email helbidea ez da aldatu."
#: libs/localization.py:61 #: libs/localization.py:68
msgid "Your email has been successfully updated." msgid "Your email has been successfully updated."
msgstr "Zure emaila ongi eguneratu da." msgstr "Zure emaila ongi eguneratu da."
#: libs/localization.py:62 #: libs/localization.py:69
msgid "The password entered is the same as the current password." msgid "The password entered is the same as the current password."
msgstr "Sartutako pasahitza egungo pasahitzaren berdina da." msgstr "Sartutako pasahitza egungo pasahitzaren berdina da."
#: libs/localization.py:63 #: libs/localization.py:70
msgid "Password has been changed!" msgid "Password has been changed!"
msgstr "Pasahitza eguneratua izan da!" msgstr "Pasahitza eguneratua izan da!"
#: libs/localization.py:64 #: libs/localization.py:71
msgid "Please, type your username for account deletion." msgid "Please, type your username for account deletion."
msgstr "Kontua ezabatzeko idatzi zure erabiltzaile izena, mesedez." msgstr "Kontua ezabatzeko idatzi zure erabiltzaile izena, mesedez."
#: libs/localization.py:65 #: libs/localization.py:72
msgid "Account successfully deleted!" msgid "Account successfully deleted!"
msgstr "Kontua ongi ezabatu da!" msgstr "Kontua ongi ezabatu da!"
#: libs/localization.py:66 #: libs/localization.py:73
msgid "Username or password is incorrect!" msgid "Username or password is incorrect!"
msgstr "Erabiltzaile izena edo pasahitza okerrak dira!" msgstr "Erabiltzaile izena edo pasahitza okerrak dira!"
#: libs/localization.py:67 #: libs/localization.py:74
msgid "Unable to connect to the remote server." msgid "Unable to connect to the remote server."
msgstr "Ezinezkoa urruneko zerbitzara konektatzea." msgstr "Ezinezkoa urruneko zerbitzara konektatzea."
#: libs/localization.py:68 #: libs/localization.py:75
msgid "" msgid "Encountered an unexpected error while communicating with the remote server."
"Encountered an unexpected error while communicating with the remote server."
msgstr "Ezusteko errore bat gertatu da urruneko zerbitzariarekin komunikatzean." msgstr "Ezusteko errore bat gertatu da urruneko zerbitzariarekin komunikatzean."
#: libs/localization.py:69 #: libs/localization.py:76
msgid "User already exists." msgid "User already exists."
msgstr "Erabiltzaile hori existitzen da." msgstr "Erabiltzaile hori existitzen da."
#: libs/localization.py:70 #: libs/localization.py:77
msgid "Email already exists." msgid "Email already exists."
msgstr "Email hori existitzen da." msgstr "Email hori existitzen da."
#: libs/localization.py:71 #: libs/localization.py:78
msgid "Forgot your password? Please try again." msgid "Forgot your password? Please try again."
msgstr "Zure pasahitza ahaztu duzu? Saia zeitez berriz, mesedez." msgstr "Zure pasahitza ahaztu duzu? Saia zeitez berriz, mesedez."
#: libs/localization.py:72 #: libs/localization.py:79
msgid "The session has expired." msgid "The session has expired."
msgstr "Saioa iraungi egin da." msgstr "Saioa iraungi egin da."
#: libs/localization.py:73 #: libs/localization.py:80
msgid "Registration is currently closed. We apologize for the inconvenience." msgid "Registration is currently closed. We apologize for the inconvenience."
msgstr "Izen-ematea itxita dago une honetan. Barkatu eragozpenak." msgstr "Izen-ematea itxita dago une honetan. Barkatu eragozpenak."
#: libs/localization.py:81
msgid "Two factor authentication has been impossible."
msgstr "Ezinezkoa izan da bi urratseko autentifikazioa burutzea."
#: libs/localization.py:82
msgid "Two factor authentication has been restored."
msgstr "Bi urratseko autentifikazioa birgaitua izan da."
#: libs/localization.py:83
msgid "Two factor authentication has been enabled."
msgstr "Bi urratseko autentifikazioa gaitua izan da."
#: libs/localization.py:84
msgid "Two factor authentication has been disabled."
msgstr "Bi urratseko autentifikazioa desgaitua izan da."
#: libs/localization.py:85
msgid "Two factor authentication was already disabled."
msgstr "Bi urratseko autentifikazioa desgaiturik zegoen."
+4
View File
@@ -1,3 +1,4 @@
Beaker>=1.12.1
bottle>=0.12.19 bottle>=0.12.19
bottle-beaker>=0.1.3 bottle-beaker>=0.1.3
ldap3>=2.9.1 ldap3>=2.9.1
@@ -5,3 +6,6 @@ uwsgi>=2.0.21
pyyaml>=6.0 pyyaml>=6.0
ua-parser>=0.16.1 ua-parser>=0.16.1
user-agents>=2.2.0 user-agents>=2.2.0
cryptocode==0.1
onetimepass==1.0.1
segno==1.5.3
+6 -3
View File
@@ -1,9 +1,9 @@
#!/bin/bash
############################## ##############################
## Erabilera: ## ## Erabilera: ##
## sudo chmod +x start.sh ## ## sudo chmod +x start.sh ##
## ./start.sh $UID ## ## ./start.sh $UID ##
############################## ##############################
#!/bin/bash
if [ ! -f settings.ini ]; then if [ ! -f settings.ini ]; then
cp settings.ini.example settings.ini cp settings.ini.example settings.ini
fi fi
@@ -11,8 +11,11 @@ fi
if [[ $# -gt 0 ]]; then if [[ $# -gt 0 ]]; then
UID_=$1 UID_=$1
echo $UID_ echo $UID_
export LDAP_ADMIN_PASSWORD=admin else
export LDAP_READONLY_PASSWORD=readonly UID_=$UID
fi fi
export LDAP_ADMIN_PASSWORD=admin
export LDAP_READONLY_PASSWORD=readonly
uwsgi --http :9090 --enable-threads --uid $UID_ --wsgi-file app.py uwsgi --http :9090 --enable-threads --uid $UID_ --wsgi-file app.py
+25
View File
@@ -194,6 +194,22 @@ button.red:hover{
animation-name: fadeOut; animation-name: fadeOut;
} }
/**/
.qr-code {
margin: 0 auto;
width: max-content;
text-align: center;
}
/**/
.info {
margin: 0 auto;
max-width: 24rem;
padding: 0 2.5rem 0 2.5rem;
text-align: justify;
}
/**/ /**/
.grid-container { .grid-container {
display: grid; display: grid;
@@ -227,6 +243,15 @@ button.red:hover{
border: 0; border: 0;
} }
.info {
max-width: 16rem;
}
}
@media only screen and (min-width: 481px) and (max-width: 760px) {
.info {
max-width: 22rem;
}
} }
/* /*
View File
+1 -1
View File
@@ -55,7 +55,7 @@
<div class="grid-item"> <div class="grid-item">
<div class="account"> <div class="account">
<h5>2FA</h5> <h5>2FA</h5>
<p>Bi urratseko autentifikazioa</p> <p>{{ str['2fa-title'] }}</p>
</div> </div>
</div> </div>
+1
View File
@@ -4,5 +4,6 @@
http = :8080 http = :8080
chdir = %v chdir = %v
wsgi-file = %v/app.py wsgi-file = %v/app.py
enable-thread = true
processes = 1 processes = 1
threads = 2 threads = 2