mirror of https://gitlab.com/bashrc2/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