Password now hashed in app.

refactor
blallo 2019-07-13 11:39:06 +02:00
parent 2b46eb0353
commit 79f682cbb7
Signed by: blallo
GPG Key ID: 0CBE577C9B72DC3F
5 changed files with 75 additions and 24 deletions

View File

@ -22,11 +22,13 @@ from phi.async_ldap.model import (
Service, Service,
create_new_, create_new_,
User, User,
PhiUserExists, PhiEntryExists,
PhiUserDoesNotExist, PhiEntryDoesNotExist,
PhiAttributeMissing, PhiAttributeMissing,
PhiUnauthorized,
Group, Group,
) )
from phi.security import hash_pass
BASE_DN = "dc=test,dc=abbiamoundominio,dc=org" BASE_DN = "dc=test,dc=abbiamoundominio,dc=org"
@ -41,7 +43,17 @@ EXISTING_USER = [
"sn": ["Existing User"], "sn": ["Existing User"],
"mail": ["existing@mail.org"], "mail": ["existing@mail.org"],
"uid": ["existing_user"], "uid": ["existing_user"],
"userPassword": ["{SHA}oLY7P6V+DWAMJ9ix7vbMYGIfA+E="], "userPassword": ["{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g="],
}
]
MISSING_AUTH_USER = [
{
"dn": f"uid=existing_user,ou=Hackers,{BASE_DN}>",
"objectClass": ["inetOrgPerson", "organizationalPerson", "person", "top"],
"cn": ["exists"],
"sn": ["Existing User"],
"mail": ["existing@mail.org"],
"uid": ["existing_user"],
} }
] ]
@ -51,6 +63,7 @@ class MockClient:
self._base_dn = base_dn self._base_dn = base_dn
self.args = args self.args = args
self.called_with_args = {} self.called_with_args = {}
self.username = f"uid=mock_connection,ou=Services,{base_dn}"
@property @property
def base_dn(self): def base_dn(self):
@ -72,6 +85,8 @@ class MockClient:
return EXISTING_USER return EXISTING_USER
elif f"uid=not_existing,ou=Hackers,{BASE_DN}" in args: elif f"uid=not_existing,ou=Hackers,{BASE_DN}" in args:
return [] return []
elif f"uid=missing_auth_user,ou=Hackers,{BASE_DN}" in args:
return MISSING_AUTH_USER
conn.search = _search conn.search = _search
@ -446,15 +461,19 @@ def test_User_singleton(client_fixture):
async def test_User_create_existing(client_fixture): async def test_User_create_existing(client_fixture):
c = User("existing_user", client_fixture) c = User("existing_user", client_fixture)
with pytest.raises(PhiUserExists): with pytest.raises(PhiEntryExists) as e:
await c.create("existing@mail.org", sn="exists", cn="Existing User") await c.create(
"existing@mail.org", password="password", sn="exists", cn="Existing User"
)
assert c.dn in str(e.value)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_User_create_not_existing(client_fixture): async def test_User_create_not_existing(client_fixture):
c = User("not_existing", client_fixture) c = User("not_existing", client_fixture)
await c.create("not@existing.org") await c.create("not@existing.org", "password")
assert client_fixture.called_with_args["search"]["args"] == (c.dn, 0) assert client_fixture.called_with_args["search"]["args"] == (c.dn, 0)
assert c._entry["mail"][0] == "not@existing.org" assert c._entry["mail"][0] == "not@existing.org"
@ -477,9 +496,10 @@ async def test_User_sync_existing(client_fixture, caplog):
async def test_User_sync_not_existing(client_fixture, caplog): async def test_User_sync_not_existing(client_fixture, caplog):
c = User("not_existing", client_fixture) c = User("not_existing", client_fixture)
with pytest.raises(PhiUserDoesNotExist): with pytest.raises(PhiEntryDoesNotExist) as e:
await c.sync() await c.sync()
assert c.dn in str(e.value)
assert client_fixture.called_with_args["search"]["args"] == (c.dn, 0) assert client_fixture.called_with_args["search"]["args"] == (c.dn, 0)

View File

@ -67,7 +67,7 @@ cn: Guido
sn: Necchi sn: Necchi
mail: gnecchi@autistici.org mail: gnecchi@autistici.org
uid: necchi uid: necchi
userPassword: {SHA}IZiNWojlNxrWIkk23XDh0svKY14= userPassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
dn: uid=perozzi,ou=Hackers,dc=unit,dc=macaomilano,dc=org dn: uid=perozzi,ou=Hackers,dc=unit,dc=macaomilano,dc=org
objectClass: inetOrgPerson objectClass: inetOrgPerson

View File

@ -2,21 +2,25 @@ from setuptools import setup, find_packages
setup( setup(
name='phi', name="phi",
version='0.0.1', version="0.0.1",
description="Post-Human Interface",
description='Post-Human Interface',
# license='', # license='',
url='https://git.abbiamoundominio.org/unit/phi', url="https://git.abbiamoundominio.org/unit/phi",
author="unit",
author='unit', author_email="unit@paranoici.org",
author_email='unit@paranoici.org', package_dir={"": "src"},
package_dir={'': 'src'},
packages=find_packages("src"), packages=find_packages("src"),
entry_points={"console_scripts": ["phid=phi.app:cli"]}, entry_points={"console_scripts": ["phid=phi.app:cli"]},
setup_requires=["pytest-runner"],
setup_requires=['pytest-runner'], install_requires=[
install_requires=['aiohttp==2.3.8', 'click==7.0', 'pyYAML', 'ldap3', 'bonsai==1.1.0'], "aiohttp==2.3.8",
tests_require=['pytest', 'pytest-aiohttp', 'pytest-asyncio', 'async-generator'] "click==7.0",
"pyYAML",
"ldap3",
"bonsai==1.1.0",
"passlib==1.7.1",
"bcrypt==3.1.7",
],
tests_require=["pytest", "pytest-aiohttp", "pytest-asyncio", "async-generator"],
) )

View File

@ -7,6 +7,7 @@ import typing as T
from bonsai import LDAPEntry, LDAPModOp, NoSuchObjectError from bonsai import LDAPEntry, LDAPModOp, NoSuchObjectError
from phi.logging import get_logger from phi.logging import get_logger
from phi.security import hash_pass
log = get_logger(__name__) log = get_logger(__name__)
@ -253,14 +254,17 @@ class User(Hackers):
def name(self, name): def name(self, name):
raise RuntimeError("Name property is not modifiable.") raise RuntimeError("Name property is not modifiable.")
async def create(self, mail, sn=None, cn=None): async def create(self, mail, password=None, sn=None, cn=None):
async with self.client.connect(is_async=True) as conn: async with self.client.connect(is_async=True) as conn:
res = await conn.search(self.dn, 0) res = await conn.search(self.dn, 0)
if len(res) > 0: if len(res) > 0:
raise PhiEntryExists(self.dn) raise PhiEntryExists(self.dn)
_sn = sn if sn is not None else self.name _sn = sn if sn is not None else self.name
_cn = cn if cn is not None else self.name _cn = cn if cn is not None else self.name
self._entry = await create_new_(self, uid=self.name, mail=mail, sn=_sn, cn=_cn) hashed = hash_pass(password)
self._entry = await create_new_(
self, uid=self.name, mail=mail, sn=_sn, cn=_cn, userPassword=hashed
)
async def sync(self): async def sync(self):
async with self.client.connect(is_async=True) as conn: async with self.client.connect(is_async=True) as conn:

View File

@ -0,0 +1,23 @@
# -*- encoding: utf-8 -*-
from passlib.hash import (
ldap_sha1,
ldap_bcrypt,
ldap_sha256_crypt,
ldap_sha512_crypt,
ldap_pbkdf2_sha256,
ldap_pbkdf2_sha512,
)
HASH_CALLABLE = {
"sha1": ldap_sha1.hash,
"bcrypt": ldap_bcrypt.hash,
"sha256_crypt": ldap_sha256_crypt.hash,
"sha512_crypt": ldap_sha512_crypt.hash,
"pbkdf2_sha256": ldap_pbkdf2_sha256.hash,
"pbkdf2_sha512": ldap_pbkdf2_sha512.hash,
}
def hash_pass(password, method="sha1"):
return HASH_CALLABLE[method](password)