Author SHA1 Message Date
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
17 changed files with 493 additions and 194 deletions
+3
View File
@@ -6,3 +6,6 @@
*~
session
libs/__pycache__
!static/tmp
static/tmp/*
!static/tmp/.gitkeep
+11 -9
View File
@@ -13,32 +13,34 @@
<body>
<main>
<h1>2FA</h1>
<h1>{{ str['2fa-title'] }}</h1>
% 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">
<label for="2fa">2FA</label>
<input id="2fa" name="2fa" type="text" value="{{data['authCode']}}">
<label for="code">{{ str['code'] }}</label>
<input id="" name="code" type="text" value="{{data['authCode']}}" readonly>
<div class="form-buttons">
<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>
</form>
% else:
<div class="info">{{ str['2fa-info-2'] }}</div>
<form name="enable2faForm" method="post" action="/enable_2fa">
<label for="2fa">2FA</label>
<input id="2fa" name="2fa" type="text" value="{{data['authCode']}}">
<!--<input id="token" name="token" type="text" value="{{data['authCode']}}" readonly>-->
<div class="form-buttons">
<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>
</form>
+149 -38
View File
@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
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.ext import beaker
from configparser import ConfigParser
@@ -27,20 +27,23 @@ from ldap3 import Server, Connection, ALL
from ldap3 import SIMPLE, SUBTREE, MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE, ALL_ATTRIBUTES
from ldap3.core.exceptions import LDAPBindError, LDAPConstraintViolationResult, \
LDAPInvalidCredentialsResult, LDAPUserNameIsMandatoryError, \
LDAPSocketOpenError, LDAPExceptionError, LDAPAttributeOrValueExistsResult
LDAPSocketOpenError, LDAPExceptionError, LDAPAttributeOrValueExistsResult, \
LDAPNoSuchAttributeResult
import logging
from os import getenv, environ, path
from os import getenv, environ, path, remove
from libs import flist, slist
from libs.localization import *
from libs.helper import tools
import random
from user_agents import parse as ua_parse
from datetime import datetime
import cryptocode
import base64
BASE_DIR = path.dirname(__file__)
LOG = logging.getLogger(__name__)
LOG_FORMAT = '%(asctime)s %(levelname)s: %(message)s'
VERSION = '0.0.2'
VERSION = '0.0.3'
@get('/')
def get_index():
@@ -52,7 +55,7 @@ def get_index():
@get('/user')
def get_index():
try:
print(newSession().get())
print('SESSION:',newSession().get())
return user_tpl(data=newSession().get(), str=i18n.str)
except Exception as e:
return index_tpl(str=i18n.str)
@@ -119,10 +122,19 @@ def get_index():
@get('/_2fa')
def get_index():
try:
reload(newSession().get()['username'], None, None)
return _2fa_tpl(data=newSession().get(), str=i18n.str)
except Exception as e:
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')
def post_user():
form = request.forms.getunicode
@@ -144,10 +156,57 @@ def post_user():
LOG.warning("Unsuccessful attempt to login %s: %s" % (form('username'), 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
sid = newSession().get()['id']
data = ';'.join([form('username'),form('password'),newSession().get()['authCode']])
data_enc = cryptocode.encrypt(data, newSession().get()['id'])
data_to_url = base64.urlsafe_b64encode(str.encode(data_enc))
memo.get(data_enc)
memo.sid=sid
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)
#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.sid)
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')
def post_signup():
@@ -198,7 +257,7 @@ def post_signup():
return error(i18n.msg[15])
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'):
return error(i18n.msg[7])
@@ -276,33 +335,45 @@ def post_edit_email():
@post('/enable_2fa')
def post_enable_2fa():
try:
username=newSession().get()['username']
add_auth_attribute_step1(username, tools.generate_secret(), action='enable')
'''
add attribute authCode
set session data
'''
except Error as e:
LOG.warning("akatsa")
return error(str(e))
return _2fa_tpl(data=newSession().get(), str=i18n.str)
def error(msg):
return _2fa_tpl(alerts=[('error', msg, 'fadeOut')], data=newSession().get(), str=i18n.str)
try:
if(not newSession().get()['secureAuth']):
try:
username=newSession().get()['username']
add_auth_attribute_step1(username, tools.gen_secret(), action='enable')
except Error as e:
reload(newSession().get()['username'], None, None)
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(alerts=[('success', i18n.msg[31], 'fadeOut')], data=newSession().get(), str=i18n.str)
@post('/disable_2fa')
def post_disable_2fa():
try:
username=newSession().get()['username']
add_auth_attribute_step1(username, tools.generate_secret(), action='disable')
'''
add attribute authCode
set session data
'''
except Error as e:
LOG.warning("akatsa")
return error(str(e))
return _2fa_tpl(data=newSession().get(), str=i18n.str)
def error(msg):
return _2fa_tpl(alerts=[('error', msg, 'fadeOut')], data=newSession().get(), str=i18n.str)
try:
if(newSession().get()['secureAuth']):
try:
username=newSession().get()['username']
add_auth_attribute_step1(username, None, action='disable')
except Error as e:
reload(newSession().get()['username'], None, None)
LOG.warning(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(alerts=[('error', i18n.msg[32], 'fadeOut')], data=newSession().get(), str=i18n.str)
@post('/change_pwd')
def post_change_pwd():
@@ -366,6 +437,10 @@ def serve_static(filename):
def font(filepath):
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):
return template('index', **kwargs)
@@ -401,8 +476,14 @@ def connect_ldap(conf, **kwargs):
return Connection(server, raise_exceptions=True, **kwargs)
@error(404)
@error(405)
def error40x(error):
return index_tpl(str=i18n.str)
#LOGIN
def login(username, password):
n = N
for key in (key for key in CONF.sections()
if key == 'ldap' or key.startswith('ldap:')):
@@ -417,7 +498,6 @@ def login(username, password):
continue
else:
raise e
break
def login_user(conf, *args):
@@ -447,9 +527,13 @@ def login_user_ldap(conf, username, password):
c.bind()
if is_trusted_device(conf, user_dn):
newSession().set(get_user_data(user_dn, c))
newSession().data['id'] = tools.session_id()
#update timestamp + ip address
update_login_info(conf, user_dn)
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
def logout(username):
@@ -702,10 +786,10 @@ def add_auth_attribute_step1(username, code, action):
try:
add_auth_attribute_step2(CONF[key], username, code, action)
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:
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:
new_email_address(CONF[key], username, new_email, old_email)
except Error as e2:
@@ -728,16 +812,18 @@ def add_auth_attribute_step2(conf, *args):
LOG.error('{}: {!s}'.format(e.__class__.__name__, e))
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:
LOG.error('{}: {!s}'.format(e.__class__.__name__, e))
raise Error(i18n.msg[23])
def add_auth_attribute_step3(conf, username, code, action):
#set current LDAP
superUser = SuperUsers(conf)
print(action)
with connect_ldap(conf, user=superUser.admin_dn, password=superUser.admin_pwd) as c:
user_dn = find_user_dn(conf, c, username)
if(action == 'enable'):
@@ -746,8 +832,18 @@ def add_auth_attribute_step3(conf, username, code, action):
elif(action == 'disable'):
c.modify(user_dn,{'authCode': [(MODIFY_DELETE, [])]})
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))
reload=add_auth_attribute_step1
#CHANGE PASSWORD
def change_passwords(username, old_pass, new_pass):
changed = []
@@ -908,6 +1004,12 @@ def get_user_email_array(user_dn, conn, old_email, new_email):
emails[i] = new_email
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):
search_filter = '(objectClass=*)'
conn.search(user_dn, search_filter,
@@ -979,7 +1081,7 @@ def is_trusted_device(conf, user_dn):
c.unbind()
return True
except Exception as e:
print(e)
LOG.warning(e)
return True
def update_login_info(conf, user_dn):
@@ -993,6 +1095,16 @@ def update_login_info(conf, user_dn):
class Error(Exception):
pass
# TEMPORAL MEMORY
class tMemory(object):
def __init__(self):
self.data = None
self.sid = None
def get(self, data):
self.data = data
memo = tMemory()
#SESSIONS MANAGEMENT
def newSession():
@@ -1001,7 +1113,6 @@ def newSession():
def __init__(self):
super(Session, self).__init__()
self.data = bottle.request.environ.get('beaker.session')
self.lang = self.get_lang()
#localization
self.lang = self.get_lang()
global i18n
Binary file not shown.
+14 -5
View File
@@ -15,12 +15,21 @@
<main>
<h1>{{ str['login'] }}</h1>
<form method="post" action="/user">
<label for="username">{{ str['usrn'] }}</label>
<input id="username" name="username" value="{{ get('username', '') }}" type="text" required autofocus>
%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">
<label for="username">{{ str['usrn'] }}</label>
<input id="username" name="username" value="{{ get('username', '') }}" type="text" required autofocus>
<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>
<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>
%end
<button class="green" type="submit">{{str['login']}}</button>
<a href="/signup">{{ str['or-sign-up'] }}</a>
-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
from onetimepass import valid_totp
from secrets import choice
import segno
from os import path
import uuid
class Tools():
@@ -45,11 +48,29 @@ class Tools():
regex = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!#%*?&]{8,18}$'
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 = ''
while len(secret) < 16:
secret += choice('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')
self.gen_qr(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 session_id(self):
return uuid.uuid4().hex
tools = Tools()
+17 -2
View File
@@ -40,6 +40,13 @@ class LocalizeTo(object):
str_25 = _("Last login")
str_26 = _("Devices")
str_27 = _("show")
str_28 = _("Two factor authentication")
str_29 = _("Token")
str_30 = _("Disable")
str_31 = _("Enable")
str_32 = _("You must then use the following code to log in. Using the key you will receive by reading the QR code via a OTP or 2FA mobile app.")
str_33 = _("Two factor 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 receive by QR.")
str_34 = _("Code")
#messages
msg_00 = _("The session was closed.")
@@ -71,11 +78,19 @@ class LocalizeTo(object):
msg_26 = _("Forgot your password? Please try again.")
msg_27 = _("The session has expired.")
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,
'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,
'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,
'show':str_27, '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)
'show':str_27, '2fa-title':str_28, 'token':str_29, 'disable':str_30, 'enable':str_31, '2fa-info-1':str_32,
'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)
+76 -28
View File
@@ -109,7 +109,7 @@ msgstr "Change your password"
msgid "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"
msgstr "Welcome"
@@ -129,35 +129,63 @@ msgstr "Devices"
msgid "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
msgid "Disable"
msgstr "Disable"
#: libs/localization.py:46
msgid "Enable"
msgstr "Enable"
#: libs/localization.py:47
msgid "You must then use the following code to log in. Using the key you will receive by reading the QR code via a OTP or 2FA mobile app."
msgstr "You must then use the following code to log in. Using the key you will receive by reading the QR code via a OTP or 2FA mobile app."
#: libs/localization.py:48
msgid "Two factor 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 receive by QR."
msgstr "Two factor 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 receive by QR."
#: libs/localization.py:49
msgid "Code"
msgstr "Code"
#: libs/localization.py:52
msgid "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!"
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."
msgstr "Not allowed characters for the username field."
#: libs/localization.py:49
#: libs/localization.py:56
msgid "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."
msgstr "Not allowed characters for the surname field."
#: libs/localization.py:51
#: libs/localization.py:58
msgid "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!"
msgstr "Passwords do not match!"
#: libs/localization.py:53
#: libs/localization.py:60
msgid ""
"The password must contain at least 8 characters, at least one number, "
"a capital letter and a special character."
@@ -165,84 +193,104 @@ 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!"
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."
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?"
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?"
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."
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."
msgstr "Invalid email address. Please try again."
#: libs/localization.py:60
#: libs/localization.py:67
msgid "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."
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."
msgstr "The password entered is the same as the current password."
#: libs/localization.py:63
#: libs/localization.py:70
msgid "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."
msgstr "Please, type your username for account deletion."
#: libs/localization.py:65
#: libs/localization.py:72
msgid "Account successfully deleted!"
msgstr "Account successfully deleted!"
#: libs/localization.py:66
#: libs/localization.py:73
msgid "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."
msgstr "Unable to connect to the remote server."
#: libs/localization.py:68
#: libs/localization.py:75
msgid ""
"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."
msgstr "User already exists."
#: libs/localization.py:70
#: libs/localization.py:77
msgid "Email already exists."
msgstr "Email already exists."
#: libs/localization.py:71
#: libs/localization.py:78
msgid "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."
msgstr "The session has expired."
#: libs/localization.py:73
#: libs/localization.py:80
msgid "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.
+84 -36
View File
@@ -1,21 +1,21 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <aitzol@lainoa.eus>, 2022.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 0.0.1\n"
"Project-Id-Version: PACKAGE VERSION\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"
"Last-Translator: Aitzol Berasategi <aitzol@lainoa.eus>\n"
"Language-Team: LANGUAGE <EN@en.org>\n"
"Language: en\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.3\n"
#: libs/localization.py:15
msgid "User"
@@ -109,7 +109,7 @@ msgstr "Change your password"
msgid "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"
msgstr "Welcome"
@@ -129,35 +129,63 @@ msgstr "Devices"
msgid "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
msgid "Disable"
msgstr "Disable"
#: libs/localization.py:46
msgid "Enable"
msgstr "Enable"
#: libs/localization.py:47
msgid "You must then use the following code to log in. Using the key you will receive by reading the QR code via a OTP or 2FA mobile app."
msgstr "You must then use the following code to log in. Using the key you will receive by reading the QR code via a OTP or 2FA mobile app."
#: libs/localization.py:48
msgid "Two factor 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 receive by QR."
msgstr "Two factor 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 receive by QR."
#: libs/localization.py:49
msgid "Code"
msgstr "Code"
#: libs/localization.py:52
msgid "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!"
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."
msgstr "Not allowed characters for the username field."
#: libs/localization.py:49
#: libs/localization.py:56
msgid "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."
msgstr "Not allowed characters for the surname field."
#: libs/localization.py:51
#: libs/localization.py:58
msgid "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!"
msgstr "Passwords do not match!"
#: libs/localization.py:53
#: libs/localization.py:60
msgid ""
"The password must contain at least 8 characters, at least one number, "
"a capital letter and a special character."
@@ -165,84 +193,104 @@ 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!"
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."
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?"
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?"
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."
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."
msgstr "Invalid email address. Please try again."
#: libs/localization.py:60
#: libs/localization.py:67
msgid "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."
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."
msgstr "The password entered is the same as the current password."
#: libs/localization.py:63
#: libs/localization.py:70
msgid "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."
msgstr "Please, type your username for account deletion."
#: libs/localization.py:65
#: libs/localization.py:72
msgid "Account successfully deleted!"
msgstr "Account successfully deleted!"
#: libs/localization.py:66
#: libs/localization.py:73
msgid "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."
msgstr "Unable to connect to the remote server."
#: libs/localization.py:68
#: libs/localization.py:75
msgid ""
"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."
msgstr "User already exists."
#: libs/localization.py:70
#: libs/localization.py:77
msgid "Email already exists."
msgstr "Email already exists."
#: libs/localization.py:71
#: libs/localization.py:78
msgid "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."
msgstr "The session has expired."
#: libs/localization.py:73
#: libs/localization.py:80
msgid "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.
+83 -38
View File
@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: 0.0.1\n"
"Report-Msgid-Bugs-To: \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-24 20:21+0100\n"
"Last-Translator: Aitzol Berasategi <aitzol@lainoa.eus>\n"
"Language-Team: LANGUAGE <EU@eu.org>\n"
"Language: eu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.3\n"
"X-Generator: Poedit 3.2.2\n"
#: libs/localization.py:15
msgid "User"
@@ -109,7 +109,7 @@ msgstr "Pasahitza eguneratu"
msgid "Delete your account"
msgstr "Kontua ezabatu"
#: libs/localization.py:38 libs/localization.py:46
#: libs/localization.py:38 libs/localization.py:53
msgid "Welcome"
msgstr "Ongi etorri"
@@ -129,119 +129,164 @@ msgstr "Gailuak"
msgid "show"
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
msgid "Disable"
msgstr "Desgaitu"
#: libs/localization.py:46
msgid "Enable"
msgstr "Gaitu"
#: libs/localization.py:47
msgid "You must then use the following code to log in. Using the key you will receive by reading the QR code via a OTP or 2FA mobile app."
msgstr ""
"Aurrerantzean ondorengo kodea erabili beharko duzu saioa hasteko. Mugikorrerako OTP edo 2FA aplikazio baten bidez QR kodea irakurtzean jasoko duzun gakoa baliatuz "
"horretarako."
#: libs/localization.py:48
msgid "Two factor 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 receive by QR."
msgstr "Bi urratseko autentifikazioa segurtasun gehigarri bat da. Hau gaituz saio hasieran erabiltzaile eta pasahitzaz gain QR bidez jasoko duzun gako bat eskatuko zaizu."
#: libs/localization.py:49
msgid "Code"
msgstr "Kodea"
#: libs/localization.py:52
msgid "The session was closed."
msgstr "Saioa itxi da."
#: libs/localization.py:47
#: libs/localization.py:54
msgid "Username must be at least 3 characters long!"
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."
msgstr "Erabiltzaile-izenaren eremurako onartzen ez diren karaktereak."
#: libs/localization.py:49
#: libs/localization.py:56
msgid "Not allowed characters for the firstname field."
msgstr "Izenaren eremurako onartzen ez diren karaktereak."
#: libs/localization.py:50
#: libs/localization.py:57
msgid "Not allowed characters for the surname field."
msgstr "Abizenaren eremurako onartzen ez diren karaktereak."
#: libs/localization.py:51
#: libs/localization.py:58
msgid "The code is invalid or has expired."
msgstr "Kodea baliogabea da edo iraungi egin da."
#: libs/localization.py:52
#: libs/localization.py:59
msgid "Passwords do not match!"
msgstr "Pasahitzak ez datoz bat!"
#: 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 ""
"Pasahitzak gutxienez 8 karaktere izan behar ditu, zenbaki bat, hizki larri bat "
"eta karaktere berezi bat."
#: libs/localization.py:60
msgid "The password must contain at least 8 characters, at least one number, 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!"
msgstr "Zorionak, zure kontua sortu da!"
#: libs/localization.py:55
#: libs/localization.py:62
msgid "Your first and last name have not been changed."
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?"
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?"
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."
msgstr "Zure izen-abizenak ongi eguneratu dira."
#: libs/localization.py:59
#: libs/localization.py:66
msgid "Invalid email address. Please try again."
msgstr "Baliogabeko email helbidea. Saia zaitez berriz, mesedez."
#: libs/localization.py:60
#: libs/localization.py:67
msgid "Email address has not been changed."
msgstr "Email helbidea ez da aldatu."
#: libs/localization.py:61
#: libs/localization.py:68
msgid "Your email has been successfully updated."
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."
msgstr "Sartutako pasahitza egungo pasahitzaren berdina da."
#: libs/localization.py:63
#: libs/localization.py:70
msgid "Password has been changed!"
msgstr "Pasahitza eguneratua izan da!"
#: libs/localization.py:64
#: libs/localization.py:71
msgid "Please, type your username for account deletion."
msgstr "Kontua ezabatzeko idatzi zure erabiltzaile izena, mesedez."
#: libs/localization.py:65
#: libs/localization.py:72
msgid "Account successfully deleted!"
msgstr "Kontua ongi ezabatu da!"
#: libs/localization.py:66
#: libs/localization.py:73
msgid "Username or password is incorrect!"
msgstr "Erabiltzaile izena edo pasahitza okerrak dira!"
#: libs/localization.py:67
#: libs/localization.py:74
msgid "Unable to connect to the remote server."
msgstr "Ezinezkoa urruneko zerbitzara konektatzea."
#: libs/localization.py:68
msgid ""
"Encountered an unexpected error while communicating with the remote server."
#: libs/localization.py:75
msgid "Encountered an unexpected error while communicating with the remote server."
msgstr "Ezusteko errore bat gertatu da urruneko zerbitzariarekin komunikatzean."
#: libs/localization.py:69
#: libs/localization.py:76
msgid "User already exists."
msgstr "Erabiltzaile hori existitzen da."
#: libs/localization.py:70
#: libs/localization.py:77
msgid "Email already exists."
msgstr "Email hori existitzen da."
#: libs/localization.py:71
#: libs/localization.py:78
msgid "Forgot your password? Please try again."
msgstr "Zure pasahitza ahaztu duzu? Saia zeitez berriz, mesedez."
#: libs/localization.py:72
#: libs/localization.py:79
msgid "The session has expired."
msgstr "Saioa iraungi egin da."
#: libs/localization.py:73
#: libs/localization.py:80
msgid "Registration is currently closed. We apologize for the inconvenience."
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."
+25
View File
@@ -194,6 +194,22 @@ button.red:hover{
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 {
display: grid;
@@ -227,6 +243,15 @@ button.red:hover{
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="account">
<h5>2FA</h5>
<p>Bi urratseko autentifikazioa</p>
<p>{{ str['2fa-title'] }}</p>
</div>
</div>
+1
View File
@@ -4,5 +4,6 @@
http = :8080
chdir = %v
wsgi-file = %v/app.py
enable-thread = true
processes = 1
threads = 2