forked from indymedia/epicyon
flake8 style
parent
65d3ace372
commit
805aef6a74
146
auth.py
146
auth.py
|
@ -1,54 +1,56 @@
|
|||
__filename__="auth.py"
|
||||
__author__="Bob Mottram"
|
||||
__license__="AGPL3+"
|
||||
__version__="1.1.0"
|
||||
__maintainer__="Bob Mottram"
|
||||
__email__="bob@freedombone.net"
|
||||
__status__="Production"
|
||||
__filename__ = "auth.py"
|
||||
__author__ = "Bob Mottram"
|
||||
__license__ = "AGPL3+"
|
||||
__version__ = "1.1.0"
|
||||
__maintainer__ = "Bob Mottram"
|
||||
__email__ = "bob@freedombone.net"
|
||||
__status__ = "Production"
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import binascii
|
||||
import os
|
||||
import shutil
|
||||
import random
|
||||
|
||||
|
||||
def hashPassword(password: str) -> str:
|
||||
"""Hash a password for storing
|
||||
"""
|
||||
salt=hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
|
||||
pwdhash= \
|
||||
hashlib.pbkdf2_hmac('sha512', \
|
||||
password.encode('utf-8'), \
|
||||
salt, 100000)
|
||||
pwdhash=binascii.hexlify(pwdhash)
|
||||
return (salt+pwdhash).decode('ascii')
|
||||
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
|
||||
pwdhash = hashlib.pbkdf2_hmac('sha512',
|
||||
password.encode('utf-8'),
|
||||
salt, 100000)
|
||||
pwdhash = binascii.hexlify(pwdhash)
|
||||
return (salt + pwdhash).decode('ascii')
|
||||
|
||||
def verifyPassword(storedPassword: str,providedPassword: str) -> bool:
|
||||
|
||||
def verifyPassword(storedPassword: str, providedPassword: str) -> bool:
|
||||
"""Verify a stored password against one provided by user
|
||||
"""
|
||||
salt=storedPassword[:64]
|
||||
storedPassword=storedPassword[64:]
|
||||
pwdhash= \
|
||||
hashlib.pbkdf2_hmac('sha512', \
|
||||
providedPassword.encode('utf-8'), \
|
||||
salt.encode('ascii'), \
|
||||
100000)
|
||||
pwdhash=binascii.hexlify(pwdhash).decode('ascii')
|
||||
return pwdhash==storedPassword
|
||||
salt = storedPassword[:64]
|
||||
storedPassword = storedPassword[64:]
|
||||
pwdhash = hashlib.pbkdf2_hmac('sha512',
|
||||
providedPassword.encode('utf-8'),
|
||||
salt.encode('ascii'),
|
||||
100000)
|
||||
pwdhash = binascii.hexlify(pwdhash).decode('ascii')
|
||||
return pwdhash == storedPassword
|
||||
|
||||
def createBasicAuthHeader(nickname: str,password: str) -> str:
|
||||
|
||||
def createBasicAuthHeader(nickname: str, password: str) -> str:
|
||||
"""This is only used by tests
|
||||
"""
|
||||
authStr=nickname.replace('\n','')+':'+password.replace('\n','')
|
||||
return 'Basic '+base64.b64encode(authStr.encode('utf-8')).decode('utf-8')
|
||||
authStr = nickname.replace('\n', '') + ':' + password.replace('\n', '')
|
||||
return 'Basic ' + base64.b64encode(authStr.encode('utf-8')).decode('utf-8')
|
||||
|
||||
def authorizeBasic(baseDir: str,path: str,authHeader: str,debug: bool) -> bool:
|
||||
|
||||
def authorizeBasic(baseDir: str, path: str, authHeader: str,
|
||||
debug: bool) -> bool:
|
||||
"""HTTP basic auth
|
||||
"""
|
||||
if ' ' not in authHeader:
|
||||
if debug:
|
||||
print('DEBUG: Authorixation header does not '+ \
|
||||
print('DEBUG: Authorixation header does not ' +
|
||||
'contain a space character')
|
||||
return False
|
||||
if '/users/' not in path and \
|
||||
|
@ -57,96 +59,102 @@ def authorizeBasic(baseDir: str,path: str,authHeader: str,debug: bool) -> bool:
|
|||
if debug:
|
||||
print('DEBUG: Path for Authorization does not contain a user')
|
||||
return False
|
||||
pathUsersSection=path.split('/users/')[1]
|
||||
pathUsersSection = path.split('/users/')[1]
|
||||
if '/' not in pathUsersSection:
|
||||
if debug:
|
||||
print('DEBUG: This is not a users endpoint')
|
||||
return False
|
||||
nicknameFromPath=pathUsersSection.split('/')[0]
|
||||
base64Str=authHeader.split(' ')[1].replace('\n','')
|
||||
plain=base64.b64decode(base64Str).decode('utf-8')
|
||||
nicknameFromPath = pathUsersSection.split('/')[0]
|
||||
base64Str = authHeader.split(' ')[1].replace('\n', '')
|
||||
plain = base64.b64decode(base64Str).decode('utf-8')
|
||||
if ':' not in plain:
|
||||
if debug:
|
||||
print('DEBUG: Basic Auth header does not contain a ":" '+ \
|
||||
print('DEBUG: Basic Auth header does not contain a ":" ' +
|
||||
'separator for username:password')
|
||||
return False
|
||||
nickname=plain.split(':')[0]
|
||||
if nickname!=nicknameFromPath:
|
||||
nickname = plain.split(':')[0]
|
||||
if nickname != nicknameFromPath:
|
||||
if debug:
|
||||
print('DEBUG: Nickname given in the path ('+nicknameFromPath+ \
|
||||
') does not match the one in the Authorization header ('+ \
|
||||
nickname+')')
|
||||
print('DEBUG: Nickname given in the path (' + nicknameFromPath +
|
||||
') does not match the one in the Authorization header (' +
|
||||
nickname + ')')
|
||||
return False
|
||||
passwordFile=baseDir+'/accounts/passwords'
|
||||
passwordFile = baseDir+'/accounts/passwords'
|
||||
if not os.path.isfile(passwordFile):
|
||||
if debug:
|
||||
print('DEBUG: passwords file missing')
|
||||
return False
|
||||
providedPassword=plain.split(':')[1]
|
||||
passfile=open(passwordFile, "r")
|
||||
providedPassword = plain.split(':')[1]
|
||||
passfile = open(passwordFile, "r")
|
||||
for line in passfile:
|
||||
if line.startswith(nickname+':'):
|
||||
storedPassword=line.split(':')[1].replace('\n','')
|
||||
success=verifyPassword(storedPassword,providedPassword)
|
||||
storedPassword = line.split(':')[1].replace('\n', '')
|
||||
success = verifyPassword(storedPassword, providedPassword)
|
||||
if not success:
|
||||
if debug:
|
||||
print('DEBUG: Password check failed for '+nickname)
|
||||
print('DEBUG: Password check failed for ' + nickname)
|
||||
return success
|
||||
print('DEBUG: Did not find credentials for '+nickname+' in '+passwordFile)
|
||||
print('DEBUG: Did not find credentials for ' + nickname +
|
||||
' in ' + passwordFile)
|
||||
return False
|
||||
|
||||
def storeBasicCredentials(baseDir: str,nickname: str,password: str) -> bool:
|
||||
|
||||
def storeBasicCredentials(baseDir: str, nickname: str, password: str) -> bool:
|
||||
"""Stores login credentials to a file
|
||||
"""
|
||||
if ':' in nickname or ':' in password:
|
||||
return False
|
||||
nickname=nickname.replace('\n','').strip()
|
||||
password=password.replace('\n','').strip()
|
||||
nickname = nickname.replace('\n', '').strip()
|
||||
password = password.replace('\n', '').strip()
|
||||
|
||||
if not os.path.isdir(baseDir+'/accounts'):
|
||||
os.mkdir(baseDir+'/accounts')
|
||||
if not os.path.isdir(baseDir + '/accounts'):
|
||||
os.mkdir(baseDir + '/accounts')
|
||||
|
||||
passwordFile=baseDir+'/accounts/passwords'
|
||||
storeStr=nickname+':'+hashPassword(password)
|
||||
passwordFile = baseDir + '/accounts/passwords'
|
||||
storeStr = nickname + ':' + hashPassword(password)
|
||||
if os.path.isfile(passwordFile):
|
||||
if nickname+':' in open(passwordFile).read():
|
||||
if nickname + ':' in open(passwordFile).read():
|
||||
with open(passwordFile, "r") as fin:
|
||||
with open(passwordFile+'.new', "w") as fout:
|
||||
with open(passwordFile + '.new', "w") as fout:
|
||||
for line in fin:
|
||||
if not line.startswith(nickname+':'):
|
||||
if not line.startswith(nickname + ':'):
|
||||
fout.write(line)
|
||||
else:
|
||||
fout.write(storeStr+'\n')
|
||||
os.rename(passwordFile+'.new', passwordFile)
|
||||
fout.write(storeStr + '\n')
|
||||
os.rename(passwordFile + '.new', passwordFile)
|
||||
else:
|
||||
# append to password file
|
||||
with open(passwordFile, "a") as passfile:
|
||||
passfile.write(storeStr+'\n')
|
||||
passfile.write(storeStr + '\n')
|
||||
else:
|
||||
with open(passwordFile, "w") as passfile:
|
||||
passfile.write(storeStr+'\n')
|
||||
passfile.write(storeStr + '\n')
|
||||
return True
|
||||
|
||||
def removePassword(baseDir: str,nickname: str) -> None:
|
||||
|
||||
def removePassword(baseDir: str, nickname: str) -> None:
|
||||
"""Removes the password entry for the given nickname
|
||||
This is called during account removal
|
||||
"""
|
||||
passwordFile=baseDir+'/accounts/passwords'
|
||||
passwordFile = baseDir + '/accounts/passwords'
|
||||
if os.path.isfile(passwordFile):
|
||||
with open(passwordFile, "r") as fin:
|
||||
with open(passwordFile+'.new', "w") as fout:
|
||||
with open(passwordFile + '.new', "w") as fout:
|
||||
for line in fin:
|
||||
if not line.startswith(nickname+':'):
|
||||
if not line.startswith(nickname + ':'):
|
||||
fout.write(line)
|
||||
os.rename(passwordFile+'.new', passwordFile)
|
||||
os.rename(passwordFile + '.new', passwordFile)
|
||||
|
||||
def authorize(baseDir: str,path: str,authHeader: str,debug: bool) -> bool:
|
||||
|
||||
def authorize(baseDir: str, path: str, authHeader: str, debug: bool) -> bool:
|
||||
"""Authorize using http header
|
||||
"""
|
||||
if authHeader.lower().startswith('basic '):
|
||||
return authorizeBasic(baseDir,path,authHeader,debug)
|
||||
return authorizeBasic(baseDir, path, authHeader, debug)
|
||||
return False
|
||||
|
||||
|
||||
def createPassword(length=10):
|
||||
validChars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||
validChars = 'abcdefghijklmnopqrstuvwxyz' + \
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||
return ''.join((random.choice(validChars) for i in range(length)))
|
||||
|
|
Loading…
Reference in New Issue